Create a LINQ query that fetches data from two Entities - c#

I want to get to the product categories in a MVC project and use them to create a menu. The model resides on a WCF project so I have instantiated it as follows:
ServiceReference1.WSClient client = new ServiceReference1.WSClient();
My Product model is like this:
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public string ProductImagePath { get; set; }
public string Specifications { get; set; }
public string Options { get; set; }
public double? UnitPrice { get; set; }
public int? CategoryId { get; set; }
public Category Category { get; set; }
public int Stock { get; set; }
}
My Category model is like this:
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Despcription { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
I want to get the product categories like this:
public PartialViewResult Menu()
{
List<Product> products = client.GetAvailableProducts().ToList();
IEnumerable<string> categories = products
.Select(myproduct => myproduct.Category.CategoryName) // <- offending line
.Distinct()
.OrderBy(x => x);
return PartialView(categories);
}
The method GetAvailableProducts() works because I get a list of products so I know that the service is working. However, when run the application, I get a null reference exception at the Linq query(see offending line above).
It seems to me that categories has to be instantiated but then, how to construct the LINQ query so that the Category is also instantiated? Can anyone point out how to do it?
BR,
Gabriel

When you serialize the Products in the service you should use LoadWith to also serialize any linked entities. This is because the default is lazy loading and EF won't load the linked entities until accessed. When you serialize the Products, Category is not accessed. LoadWith will perform an eager load so that all data will be serialized.
Example:
public IEnumerable<Product> GetAvailableProducts()
{
var ctx = new ProductsContext();
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Product>(p => p.Category);
ctx.LoadOptions = dlo;
return ctx.Products.ToList();
}
Edit:
Guess it's too late in the afternoon. :(
LoadWith is used in conjuction with Linq to SQL.
With Entity Framework you should use Include instead:
public IEnumerable<Product> GetAvailableProducts()
{
var ctx = new ProductsContext();
return ctx.Products.Include("Category").ToList();
}
Disclaimer: Code not tested

You're pulling down the Products successfully but your service is not pulling down the related categories.
Thus, when you do myproduct.Category.CategoryName, Category is always null.
You need to tell the service to return the related categories.

It looks like your relationship is a 0 or 1 to many since CategoryId is an int?. If the Category is null, then you can't do Category.CategoryName. That's a null reference.

On your offending line, myproduct.Category, there is one or more null Category. You need to change
client.GetAvailableProducts().ToList();
to also pull down the Category that each product is composed of.
You should also guard for the inevitable null Category.

Related

Projecting self referencing multi level Entities In Entity Framework 6

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>();
}
}

Query many to many relation with lambda expression

I'm using entity framework database first.
For one to many relation, in sql server, I use the following sql query(Status_ID is foreign key):
SELECT Products.*, Status.Title as Pstatus
FROM Products, Status
WHERE Products.Status_ID = Status.ID
In MVC I use the following code to retrieve the same data as above sql query and pass the list to the View:
Controller:
var products = oDB.Products.Include(m => m.Status)
.ToList();
// string test = products[0].Status.Title;
return View(products);
In the view I can access the desired data by the following code:
View:
#model List<myDB.Product>
...
#item.Status.Title // This works well for each item in the list
For MANY TO MANY RELATIONS WITH JUNCTION TABLE, this is my .edmx:
Now How could I retrieve list of products including related categories?
I need the list of products and pas it to the view as a list, and access each product's categories in the view.
My Classes (These classes are generated automatically):
public partial class Category
{
public int ID { get; set; }
public string Title { get; set; }
public virtual ICollection<Products_Categories> Products_Categories { get; set; }
}
public partial class Products_Categories
{
public int ID { get; set; }
public Nullable<int> Product_ID { get; set; }
public Nullable<int> Category_ID { get; set; }
public virtual Category Category { get; set; }
public virtual Product Product { get; set; }
}
public partial class Product
{
public int ID { get; set; }
public string Title { get; set; }
public virtual ICollection<Products_Categories> Products_Categories { get; set; }
}
var products = oDB.Products.Include(m => m.Product_Categories.Select(pc=>pc.Category))
.ToList();
// string test = products[0].Status.Title;
return View(products);
and in a view you could use it like this
#foreach(var item in model){
<h3>string.join(", ", item.Product_Categories.Select(pc=>pc.Category.Title))</h3>
}
You need to use something like this:
var products = oDB.Products.Include("Status")
.ToList();
// string test = products[0].Status.Title;
return View(products);

Navigate across relationships in EF

I'm begginer with EF, so my question is probably basic, but I couldn't find any answer...
I have a SQL Compact DB, from which I generated an entity model via the VS wizard. Everything seems fine, I retrieve all my relationships with the good mapping.
So as I understand from here: http://msdn.microsoft.com/en-us/library/bb386932(v=vs.110).aspx I should be able to do this, "querying across relationships":
IQueryable<Ingredient> IngQuery = from i in db.Ingredient
where i.Product.ID == ProdID
select i;
But I get the following error:
'System.Collections.Generic.ICollection' does not
contain a definition for 'ID' and no extension method 'ID' accepting a
first argument of type
'System.Collections.Generic.ICollection' could be found
(are you missing a using directive or an assembly reference?).
This error occurs when you try to call a method or access a class member that does not exist
However, if I go deeper into the auto-generated code, I can see a public property 'ID' is declared for 'Product', and 'Ingredient' return a collection of 'Product':
Ingredient
public partial class Ingredient
{
public Ingredient()
{
this.Product = new HashSet<Product>();
}
public string Name { get; set; }
public int ID { get; set; }
public virtual ICollection<Product> Product { get; set; }
}
Product
public partial class Products
{
public Products()
{
this.Ingredient = new HashSet<T_PROPSTHERAP>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Usage { get; set; }
public byte[] Photo { get; set; }
public int FK_OrganeProduct { get; set; }
public int FK_Type { get; set; }
public virtual OrganeProduct OrganeProduct { get; set; }
public virtual Type Type { get; set; }
public virtual ICollection<Ingredient> Ingredient { get; set; }
}
But it doesn't work as I expected.
I can use the following as workaround:
List<Ingredient> lstIng = (_oTest.Products
.Where(p => p.Name == (string)lsbProducts.SelectedItem)
.SelectMany(p => p.T_PROPSTHERAP)).ToList();
But I don't think it's a smart way to do the trick... And I don't understand what I am missing...
Could anyone help?
If I understand you correctly, you are trying to find Ingredients based on Product's ID. As you have known, the Product property is a collection, not a singular object.
What you need is filtering Products based on Product's ID, you can use Any to filter collection.
IQueryable<Ingredient> IngQuery = from i in db.Ingredient
where i.Product.Any(p => p.ID == ProdID)
select i;
That means:
Looking for Ingredient if any of its product has ID equals to ProdID.
You can also use All, if what you are looking for is:
Looking for Ingredient if all of its products have ID equals to ProdID.
IQueryable<Ingredient> IngQuery = from i in db.Ingredient
where i.Product.All(p => p.ID == ProdID)
select i;
PS
However, based on your workaround, using Any is what you are looking for.

object initializer

Both snippets of code work, however, I'm wondering which one is a better(if any). Is there a real difference? The part of the code I'm referring to is in the LINQ. Any insight would be appreciated.
select new Product {...
First Snippet:
public static IEnumerable<Product> GetProducts(IEnumerable<Product> products)
{
var query = from p in products
select new Product
{
Category = p.Category,
Id = p.Id,
CategoryId = p.CategoryId,
Name = p.Name
};
return query;
}
class Product
{
public int Id { get; set; }
public String Name { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
}
class Category
{
public int Id { get; set; }
public String CategoryName { get; set; }
}
Second Snippet:
public static IEnumerable<Product> GetProducts(IEnumerable<Product> products)
{
var query = from p in products
select p;
return query;
}
class Product
{
public int Id { get; set; }
public String Name { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
}
class Category
{
public int Id { get; set; }
public String CategoryName { get; set; }
}
The first snippet will return a query that will, when enumerated, create a copy of each object in the products enumerable, and iterate over those copies. (Caveat: If the input objects are of a type derived from Product and not actually Product itself, then you will "slice" those objects into instances of Product.)
The second snippet will return a query that will, when enumerated, iterate over the objects in the original sequence, and really isn't semantically different from doing return products; (assuming that products is not null, that is -- both variations would throw an exception when enumerated if products was null, but would throw a different exception type).
The "tl;dr" version is: the first approach copies the objects in the sequence, the second one does not.
Use the first when you need to make a deep copy of a sequence so that modifying the objects in the resulting sequence do not modify the objects in the original sequence. Use the second approach if you do, in fact, want modifications to affect the original sequence, or you will not be modifying either of the two sequences.
Aside: If you do need to take copies, consider creating a virtual method on the Product class (public virtual Product Clone()) so that (a) the cloning logic is encapsulated, and (b) if you derive Product, you can override Clone() to return a properly-typed copy.

Retrieving Hierarchal data in Entity Code-First

I have some hierarchal data. The Model class I use looks like this:
public class Category
{
[Key]
public int CategoryID { get; set; }
[Required]
[StringLength(64)]
public string Name { get; set; }
public int? ParentCategoryID { get; set; }
[ForeignKey("ParentCategoryID")]
public Category ParentCategory { get; set; }
[Required]
public int ListOrder { get; set; }
// left/right
public int TreeLeft { get; set; }
public int TreeRight { get; set; }
} // eo class Category
I've used the techniques outlined here to store my data, and inserting and retrieving data is not a problem.
What I would like to do, is add a Category collection to this class:
public virtual IEnumerable<Category> {get; set; }
I've used this technique in the past (learned from the Mvc tutorials), to include related tables when getting data. However, when I tried this I received an error with regard to IEnumerable<> being abstract (which is understandable, I guess the framework couldn't figure out what I want to do)...
... and indeed, being new to LINQ, I have no idea what the LINQ would look like that would give me back a collection of Category instances each of which had their children inside them.
If it's not possible I guess I can construct the list manually, use a regular LINQ query to get all the categories at a particular position (and their children) and manually populate it all.
I was wondering if LINQ could do this for me?
Thanks in advance!
If you have a self reference fk than the collection should be generated automatically when you add the table to the dbml file
And will look something like this:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Category_Category", Storage="Categories", ThisKey="pkCategoryID", OtherKey="ParentCategoryID")]
public EntitySet<Category> Categories
{
get
{
return this._Categories;
}
set
{
this._Categories.Assign(value);
}
}
Dont use IEnumerable<Category> but Collection<Category> . If that is your problem..

Categories