I have News Class as follows:
public class News
{
public virtual int Id { set; get; }
public virtual string Title { get; set; }
public virtual Category Category { set; get; }
public virtual DateTime DateCreated { set; get;}
}
and There is category class;
public class Category
{
public virtual int Id { set; get; }
public virtual string Name { set; get;}
}
News table is populated with data.
What I need is to get one News per category ordered by DateCreated. OK So I want latest news from the category, but I want all the news for all categories.
OK I can get all categories and get last News per Category, I m curious if there is an efficient way to do this?
any ideas?
What would be the proper syntax also?
In my opinion, the cleanest way to do it is adding an inverse collection to the Category class (this is technically free, let me know if you need help with the mapping):
public virtual ICollection<News> NewsItems { get; set; }
And then it's a simple LINQ query:
var results = from category in session.Query<Category>()
select new
{
category,
LatestNews = category.NewsItems
OrderByDescending(x => x.DateCreated).FirstOrDefault()
};
Related
I'm using MVC, C# and EntityFramework.
I've seen different solutions on Many to Many joins and after a lot of tinkering I got it to work in Linqpad. But when I try it in my solution I get an error because one of the tables isn't in my DBContext.
I have two visible tables and one hidden. Items, Recipes & RecipeItems.
All recipes are based on one item and use two or more items to be made.
So I want a list, IEnumerable or similar with the data from both Items and Recipes that specifies this recipe and then I want all the items needed to make the recipe.
The following query works in LinqPad
var t = from r in Recipes
join i in Items on r.ItemId equals i.Id
select new {FinalProduct = r.FinalProduct, Effect= i.Effect,
Description = r.Description, Ingredients = r.RecipeItems.Select(g => g.Item)};
When I do this in my solution I get the error since my DBContext only contains Recipe and Items but no RecipeItems. Entityframework handles this without me I guess.
I tried to make a DbSet<RecipeItems> without any luck. Any of you who have a suggestion of how I can move forward.
Item Class
public class Item
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Effect { get; set; }
public bool Published { get; set; }
public virtual ICollection<Recipe> Recipe { get; set; }
}
Recipe Class
public class Recipe
{
[Key]
public int Id { get; set; }
public int ItemId { get; set; }
[Display(Name = "Final Product")]
public string FinalProduct { get; set; }
public string Description { get; set; }
public RecipeGroup RecipeGroup { get; set; }
public bool Published { get; set; }
public virtual ICollection<Item> Ingredients { get; set; }
}
The ItemId in Recipe is to set the actual Item the Recipe will make.
Try adding this to your Recipe object:
public Recipe()
{
this.Ingredients = new HashSet<Item>();
}
This overrides the default constructor for the class and kind of gives EF a place that initializes the related objects.
Projecting self referencing multi level entities in Entity Framework 6.
Let's say that I have a Category entity as follows:
public class Category
{
public int CategoryId { get; set; }
public int? ParentCategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> SubCategories { get; set; }
public virtual ICollection<Product> Products { get; set; }
public Category()
{
SubCategories = new HashSet<Category>();
Products = new HashSet<Product>();
}
}
And I would like to map the whole Category DbSet with all the hierarchy to a following POCO class (while including all possible levels of sub and parent categories):
public class CategoryView
{
public int Id { get; set; }
public int? ParentCategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public CategoryView ParentCategory { get; set; }
public List<CategoryView> SubCategories { get; set; }
public int ProductCount { get; set; }
public Category()
{
SubCategories = new HashSet<CategoryView>();
}
}
Please bear in mind that a single category may have unlimited levels of subcategories as follows:
Category (Level 0)
SubCategory1 (Level 1)
SubCategory2
SubCategory2SubCategory1 (Level 2)
SubCategory2SubCategory2
SubCategory2SubCategory2SubCategory1 (Level 3)
... (Level N)
SubCategory3
When tried to create hierarchy with recursive a method which tries to process every single categories sub and parent categories, got stackoverflow exception, since it get stuck between the first category (Category) and the first subcategory (SubCategory1) due to relation between ParentCategory and SubCategories.
What is the best and elegant way of doing such projection (without eliminating parents)? (Or is there any?)
Any help would be appreciated.
Thank you,
I can't say if it's the best or elegant way, but it's pretty standard and efficient non recursive way of building such structure.
Start with loading all categories without parent / child object links using a simple projection:
var allCategories = db.Categories
.Select(c => new CategoryView
{
Id = c.CategoryId,
ParentCategoryId = c.ParentCategoryId,
Name = c.Name,
Description = c.Description,
ProductCount = c.Products.Count()
})
.ToList();
then create a fast lookup data structure for finding CategoryView by Id:
var categoryById = allCategories.ToDictionary(c => c.Id);
then link the subcategories to their parents using the previously prepared data structures:
foreach (var category in allCategories.Where(c => c.ParentCategoryId != null))
{
category.ParentCategory = categoryById[category.ParentCategoryId.Value];
category.ParentCategory.SubCategories.Add(category);
}
At this point, the tree links are ready. Depending of your needs. either return the allCategories or the root categories if you need a real tree representation:
return allCategories.Where(c => c.ParentCategoryId == null);
P.S. Actually the allCategories list can be avoided, since categoryById.Values could serve the same purpose.
It might not be elegant, but a suitable solution is to have in your code a shared IDictionary<int, CategoryView>. When you are going to map an entity Category into a CategoryView check first if you have already created this object and set the reference stored in the dictionary instead of creating a CategoryView instance. When creating a new instance, store it in the dictionary. This is a way to take advantage of the primary key of your entity to avoid the infinite recursion issue in your code.
Also, notice that in your CategoryView object you shouldn't be referencing Category instances. Update it to reference CategoryView instances like this.
public class CategoryView
{
public int Id { get; set; }
public int? ParentCategoryId { get; set; }
// other properties ...
public CategoryView ParentCategory { get; set; }
public List<CategoryView> SubCategories { get; set; }
public int ProductCount { get; set; }
public CategoryView()
{
SubCategories = new List<CategoryView>();
}
}
I have been trying to work this out for a while now and can't find an answer that makes sense to me. The concept is very common, so I must be totally misunderstanding a basic concept.
If I have a recipe class that can be found in many recipe categories then I have;
public class Recipe
{
public int ID { get; set; }
public string Title { get; set; }
public int CategoryID { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Description { get; set; }
public int RecipeID {get; set;
public virtual void Recipe Recipe {get; set;}
But I also need a join table that relates a recipe to a Category. I want to display this;
Recipe Title | Category
Mac-N-Cheese | Pasta
| Easy
Pot Roast | Beef
| Slow cooker
The Category is a table of available categories. So the join table has
RecipeID | CategoryID
I tried setting up the models using the Entity Framework format of foreign keys and navigation properties.
So I set up the join table like this;
public class RecipeCategories
{
public int ID { get; set; }
public int RecipeID { get; set; }
public int CategoryID { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
So a recipe can have many categories. The join table can have many recipes and many categories. The category table is just a simple list of categories. What am I missing? When I try to run the view there is no list of categories for the given recipe. The best I have achieved is the CategoryID.
Sorry for the long post, but you need all the details.
The problem is your Category model. The way you've currently defined it you have a one-to-many relationship between Category and Recipe (a Recipe can have many Categories but a Category one has a single Recipe). What you want is a many-to-many relationship so put a collection of Recipes on the Category and EF should automatically generate the join table.
public class Recipe
{
public int ID { get; set; }
public string Title { get; set; }
public int CategoryID { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Description { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
I am no getting actual scenario as you trying to achieve.
as you mention your model and then what you expected both contradict. as per you mention you have recipe and category (one to many) but later you change your model and mention you want (many to many) so you need join table. as a relation model you can handle 2 ways in EF. 1st without creating separate table and keep collection map to each model and 2nd way creating separate table and explicitly map that in your model. you need to explicitly specify while model building.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Recipe>()
.HasMany(c => c.Categories).WithMany(i => i.Recipes)
.Map(t => t.MapLeftKey("RecipeID")
.MapRightKey("CategoryID")
.ToTable("ReceipeCategory")
);
you can define your class model (and third table will generate but don't required specific model )
class Recipe {.... public virtual ICollection<Category> Categories { get; set; }
class Category {... public virtual ICollection<Recipe> Recipes { get; set; } }
Working in one project (Catering theme ) when I was designing the database I didn't take care about some thing , and now Is very hard to avoid some kine of errors(Circular error).
Suppose I have following scenario :
I have Meal object that should be composed from a list of semi-finished products (we will call it Product ) and list of simple Resources.
One Product is composed from a list of Resoruces and list of products.
So in real example this will look like this:
Meal: Pizza that contains list of Resoruces(cheese,dough) and list of Products : in our case will be just :Sauce.
Sauce will be composed from List of Resources(salt,Some cheese ,tomato Sauce) and a List of Products (in our case will be just one "Chopped tomatoes with salt")
So now I have following classes:
public class Resource
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductToProduct
{
public int Id { get; set; }
public Product MainProduct { get; set; }
public Product Component { get; set; }
public double Quantity { get; set; }
}
public class ProductToResource
{
public int Id { get; set; }
public Product Product { get; set; }
public Resource Resource { get; set; }
public double Quantityt { get; set; }
}
public class Meal
{
public int Id { get; set; }
public string Name { get; set; }
public IList<MealToProduct> MealToProducts { get; set; }
public IList<MealToResource> MealToResources { get; set; }
}
public class MealToResource
{
public int Id { get; set; }
public Meal Meal { get; set; }
public Resource Resource { get; set; }
public double Quantity { get; set; }
}
public class MealToProduct
{
public Meal Meal { get; set; }
public Product Product { get; set; }
public double Quantity { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ProductToResource> ProdcutToResources { get; set; }
public IList<ProductToResource> ProductToProducts { get; set; }
}
My problem is in relation between Product to Product.
Suppose I will have Product1, Product2 , Product3 , Product4.
Product 1 will be composed from something and Prodcut2, Product4.
Product2 will be composed from something and Prodcut3.
Prodcut 3 will be composed from something and Prodcut4.
Prodcut 4 will be composed from something and Prodcut1 , in this case when I will try to calcualte Cost for Product1 , or Product 4 I will get an Circular error.
So my problem is in ProductToProduct table.
My question is how I must to design tables to avoid this kind of errors .
I AM VERY SORRY FOR MY EXPLICATION BUT IT IS VERY HARD TO EXPLAIN THIS PROBLEM.
PLEASE ASK ME IF SOMETHING IS UNCLEAR.
THANKS FOR YOUR ATTENTION.
Note:This is not so important for this case but I am working in ASP.Net mvc , orm is Fluent Nhibernate.
Here's an example of a function you could use to detect whether a parent-child relationship exists. I have assumed that the product relationships are described in a table called ProductLink, which has two foreign keys to Product: ParentProductId and ChildProductId.
This function uses a recursive query to determine the complete list of products which are children of the product denoted by the argument #ParentProductId, then does a simple test to see whether #ChildProductId appears in that list.
create function dbo.ProductRelationshipExists
(
#ParentProductId int,
#ChildProductId int
)
returns bit
as
begin
declare #ChildExists bit = 0;
with ProductChildCTE as
(
-- Base case: Get the parent's direct children.
select ChildProductId from ProductLink where ParentProductId = #ParentProductId
-- Recursive case: Get the children's children.
union all
select
ProductLink.ChildProductId
from
ProductChildCTE
inner join ProductLink on ProductChildCTE.ChildProductId = ProductLink.ParentProductId
)
select #ChildExists = 1 from ProductChildCTE where ChildProductId = #ChildProductId;
return #ChildExists;
end
When someone tries to insert a record into ProductLink, you could use a test like this to determine whether the proposed parent and child are already in the table as child and parent, respectively, and disallow the insertion if so.
This was just a quick write-up to illustrate one possible approach; I should mention that I don't know how well the performance of this thing will scale as the table gets larger. Hopefully it will suffice for your case. If not, let me know how to improve it.
I have two simple classes:
class Student
{
public int StudentId { get; set; }
public int IndeksNo { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public virtual Group Group { get; set; }
}
And
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual List <Student > Students { get; set; }
}
Database is created correctly (using Code First). I have added couple items to both tables, but all the time Students list and Group property in Student are null. I have no idea why. I have searched solution for about an hour and i came up with something like this:
modelBuilder.Entity<Student>()
.HasRequired(st => st.Group)
.WithMany(gr => gr.Students)
.WillCascadeOnDelete(false);
But it doesn't help. I have no idea what may went wrong or why Group.Students and Student.Group are always null. List of groups and list of students are selected from db successfully - i mean all params except those connections.
In order to use the Lazy Loading feature of EntityFramework. Your navigation property must be virtual. In your Student class in the the case. The problem is with your Group class. The navigational property is a virtual List<Student> it must be a virtual ICollection<Student>
You can simply change your Group to
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<Student > Students { get; set; }
}