I am so very new to c# and am learning as I go...
I have two tables, Blog and BlogCategories. The Blog table has an id that references the category in BlogCategories.
I am having an issue with the syntax on the join:
var categories = new List<BlogCategory>();
if (model.BlogCategoryId.HasValue)
{
var query =
from category in categories
join blog in model on category.Id equals model.BlogCategoryId
select new { BlogCategory = category.Name };
}
The issue is that it doesn't like the join and get :
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'
Any help would be greatly appreciated... PHP is not a big boy language, this is.
Quick table structure
BlogPosts table:
Id,
BlogCategory,
Title,
Content
BlogCategories:
Id,
CategoryName
EDIT:
Solution seems like it would work but error somewhere:
var blogPosts = PopulateBlogPosts();
if (model.BlogCategoryId.HasValue)
{
var blogPostCategories = PopulateBlogCategories();
blogPosts = blogPostCategories.Where(c => c.Id == model.BlogCategoryId).Single();
}
Assuming categories is an IEnumerable<BlogCategory>.
var category = categories.Single(c => c.Id == model.BlogCategoryId);
This Single extension method asserts you're only expecting one object to match, and it allows you to filter. It will throw an exception if there is anything other than one category that meets the filter criteria. It's a shorter form of this:
var category = categories.Where(c => c.Id == model.BlogCategoryId).Single();
Related
Was difficult for me to find a fitting title for this post. But I have the following:
IArticleRepository articleRepo = unitOfWork.ArticleRepository;
List<Article> articles = new List<Article>(
articleRepo.GetAll()
.Where(a => a.Title == searchTerm)
//.Where(a => a.Categories.Contains(Category.))
.OrderByDescending(a => a.CreatedDate));
So some explanation: An article has , among other things, a Title and a CreateDate, and filtering through those is easy. But an article also has categories associated with it. So an article has an array property of type Category. Type Category has a property called CategoryId of type int.
So in my code where it's commented out, I'm trying to select an article, which has a category associated with it, who's CategoryId is equal to.. say 4.
But I'm finding it quite difficult to express this in my C# syntax. I'm also new to C# so that's not helping either.
You don't need to write two Where clauses; just add another condition to your first Where. The second condition should use Any function to search for the categories you're looking for.
IArticleRepository articleRepo = unitOfWork.ArticleRepository;
List<Article> articles = new List<Article>(
articleRepo.GetAll()
.Where(a => a.Title == searchTerm &&
a.Categories.Any(c => c.CategoryID == 4))
.OrderByDescending(a => a.CreatedDate));
For multiple categories, suppose you have your CategoryIDs in an int[] or List<int> named MyCatIDsList. They you can change the categories clause in the above query to this:
a.Categories.Any(c => MyCatIDsList.Contains(c.CategoryID))
There is an alternative syntax when using LINQ queries, which is more like SQL. The code above is correct, but you may find this version more concise:
int categoryId = 4
IArticleRepository articleRepo = unitOfWork.ArticleRepository;
var articlesQuery = from article in articleRepo.GetAll()
from category in article.Categories
where category.CategoryId == categoryId
where article.Title == searchTerm
orderby article.CreatedDate descending
select article
List<Article> articles = articlesQuery.ToList();
Or its more common to do these all together in one step:
int categoryId = 4
List<Article> articles = (
from article in articleRepo.GetAll()
from category in article.Categories
where category.CategoryId == categoryId
where article.Title == searchTerm
orderby article.CreatedDate descending
select article
).ToList()
You don't need to create a new list and you can use several where expressions in one Where clause. Can you try the following code:
List<Article> articles = articleRepo.GetAll()
.Where(a => a.Title == searchTerm && a.Categories.Contains(Category)).OrderByDescending(a => a.CreatedDate)).ToList();
I think this is kind of a basic question but I'm getting confused. I have two objects, Orders and OrderTags. In the database, Orders has no relation to OrderTags, but OrderTags has a FK relation to Orders.
So I capture both objects in my context like so:
orders = context.Orders;
tags = context.OrderTags.Where(tag=> tag.ID = myID);
Now I want to reduce the orders list to only be equal to the orders that exist in my tags list. Here is my best pseudocode of what I want to do:
orders = orders.Where(every order id exists somewhere in the tags list of order ids)
For clarification, each Tag object has a TagID and an OrderID. So I only want the orders that correspond to the tags I have looked up. Can anyone assist me with the syntax so I can get what I'm looking for?
Using a LINQ query:
var results = (from o in context.Orders
join t in context.Tags on o.OrderId equals t.OrderId
where t.ID == myID
select o ).ToList();
Using LINQ query:
orders = orders.Where(order => tags.Contains(tag => tag.ID == order.OrderID)).ToList();
Using a LINQ query with lambda expressions:
orders.RemoveAll(x => !tags.ConvertAll(y => y.tagId).Contains(x.tagID));
Something like this should work.
orders = orders.Where(o=>tags.Contains(t=>o.ID == t.OrderID));
You could also just perform a join.
In a project i have a LINQ to SQL class which is OrderingSystem.dbml. I have the following code and in the select statement i want to retrieve only some of the rows from each table(Products - Categories). But of course the return statement that i have right now it is not correct. What is the correct return statement to use?? Thanks in advance
OrderingSystemDataContext database = new OrderingSystemDataContext();
public List<Product> GetProductsByCategoryID(int CategoryID)
{
var result = from p in database.Products
join c in database.Categories
on p.CategoryID equals c.CategoryID
where p.CategoryID == CategoryID
select new { p.ProductName, p.ProductPrice, p.ProductDescription, c.CategoryName };
//This is not working
return result.ToList();
}
On the select new you have to specify the type if you then want to retrieve a list of products, right now it creates an anonymous type. Try changing with :
OrderingSystemDataContext database = new OrderingSystemDataContext();
public List<Product> GetProductsByCategoryID(int CategoryID)
{
var result = from p in database.Products
join c in database.Categories
on p.CategoryID equals c.CategoryID
where p.CategoryID == CategoryID
//Assuming that these are the names of your properties
select new Product(){ProductName = p.ProductName, ProductPrice = p.ProductPrice, ProductDescription = p.ProductDescription, CategoryName = c.CategoryName };
return result.ToList();
}
It is because in the LINQ expression, you did a select new { } which creates an anonymous object, while your method returns a List of Product. You should change the select statement so it becomes select new Product() { ProductName = p.ProductName, ... the rest depends on the structure of your Product class.
By using the "select new" LINQ operation, you are creating an anonymous type. You can access the properties of that anonymous type within the current scope of the objects' creation, but you cannot return that anonymous type using any kind of recognizable type. You have two options:
Create a new class (e.g. Product) and create instances of that type as part of your select statement (as the other answers instruct)
Return the collection as List<object>. If you do this, you can still retrieve values from the objects, but you will need to do so via reflection. This can be quite valid if you are using the collection as the data source in a data binding scenario.
Your return type is Product but your query result uses an anonymous type.
Change this line:
select new { p.ProductName, p.ProductPrice, p.ProductDescription, c.CategoryName };
to something like this:
select new Product { Name = p.ProductName, Price = p.ProductPrice, Description = p.ProductDescription, Category = c.CategoryName };
EDIT:
Try just replacing it with:
select p;
I don't know how can I return posts to my view where their tags are equal to those who are passed to controller action.
I think there is some clever and easy way of doing this but I am very new to LINQ and SQL.
Code
// id = tag name, not it's id
public ActionResult Tag(string id)
{
// I get all the PostTags where PostTags.Tag.Name = id
var postTags = _db.PostTags.Where(x => x.Tag.Name == id);
// And what I do now?
}
Using joins in relational data is easier to grasp as a novice using query syntax instead of extension methods. The following is possible with extension methods (like .Join(...), etc), but this is closer to the SQL you might already be used to.
var postTags = from t in _db.Tags
join pt in _db.PostTags on t.ID equals pt.TagID
join p in _db.Posts on pt.PostID equals p.ID
where t.Name == id
select p;
Well to select them you could do something similar to..
var posts = _db.Posts.Where(post => post.PostTags.Any(postTag => postTag.Tag.Name == id));
This will just select all Posts where any of the related PostTags has a Tag with the name passed.
var query =context.Categories.Include("ChildHierarchy")
.Where(c =>
context.CategoryHierarchy.Where(ch => ch.ParentCategoryID == ch.ParentCategoryID)
.Select(ch => ch.ChildCategoryID).Contains(c.CategoryID));
Questions:
I need to include some data from another Navigation Propery (".Include("otherprop")")
Is it possible to do a select new after all of this?
Thanks
The title to your question intrigued me with the words "Crazy Query", and yes, you're right, it is a bit crazy.
You have a .Where(...) clause with the following predicate:
ch => ch.ParentCategoryID == ch.ParentCategoryID
Now that's going to always be true. So I guess that you're trying to do something else. I'll have a crack at what that might be at the end of my answer.
I then did some cleaning up of your query to get a better idea of what you're doing. This is what it now looks like:
var query =
context
.Categories
.Where(c => context
.CategoryHierarchy
.Select(ch => ch.ChildCategoryID)
.Contains(c.CategoryID));
So rather than use nested queries I would suggest something like this might be better in terms of readability and possibly performance:
var query =
from c in context.Categories
join h in context.CategoryHierarchy
on c.CategoryID equals h.ChildCategoryID into ghs
where ghs.Any()
select c;
This gives the same results as your query so hopefully this is helpful.
I do get the impression that you're trying to do a query where you want to return each Category along with any child categories it may have. If that's the case here are the queries you need:
var lookup =
(from c in context.Categories
join h in context.CategoryHierarchy
on c.CategoryID equals h.ChildCategoryID
select new { ParentCategoryID = h.ParentCategoryID, Category = c, }
).ToLookup(x => x.ParentCategoryID, x => x.Category);
var query =
from c in context.Categories
select new { Category = c, Children = lookup[c.CategoryID], };
The lookup query first makes a join on categories and the category hierarchies to return all children categories and their associated ParentCategoryID and then it creates a lookup from ParentCategoryID to a list of associated Category children.
The query now just has to select all categories and perform a lookup on the CategoryID to get the children.
The advantage of using the .ToLookup(...) approach is that it easily allows you to include categories that don't have children. Unlike using a Dictionary<,> the lookup does not throw an exception when you use a key that it hasn't got a value for - instead it returns an empty list.
Now, you can add back in the .Include(...) calls too.
var lookup =
(from c in context.Categories
.Include("ChildHierarchy")
.Include("otherprop")
join h in context.CategoryHierarchy
on c.CategoryID equals h.ChildCategoryID
select new { ParentCategoryID = h.ParentCategoryID, Category = c, }
).ToLookup(x => x.ParentCategoryID, x => x.Category);
var query =
from c in context.Categories
.Include("ChildHierarchy")
.Include("otherprop")
select new { Category = c, Children = lookup[c.CategoryID], };
Is that what you're after?
1) Then add it - context.Categories.Include("ChildHierarchy").Include("OtherCollection");
2) Absolutely, yes
var query = context.Categories
.Include("ChildHierarchy")
.Include("OtherProp")
.Where(c => context.CategoryHierarchy.Where(ch => ch.ParentCategoryID == ch.ParentCategoryID)
.Select(ch => ch.ChildCategoryID).Contains(c.CategoryID))
.Select(c => new { c.A, c.B, c.etc });