EF6 Query across multiple many-to-many relationships - many-to-many

I have a group model that has a many-to-many relationship with two other models. Here is a simplified code first to help explain:
public class Store
{
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<StockGroup> StockGroups { get; set; }
}
public class StockItem
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<StockGroup> StockGroups { get; set; }
}
public class StockGroup
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<Store> Stores { get; set; }
public virtual ICollection<StockItem> StockItems { get; set; }
}
Using Fluent API I created a many-to-many relationship between Store and StockGroup as well as StockItem and StockGroup. This in turn creates two join tables.
The issue I am having is I cannot seem to create a query that spans from Store to StockItem or the reverse without using foreach loops.
For example: Given a single StockItem, I would like to know all the stores that are related to it through the StockGroup table. I tried something like this but what it is returning is a collection of a collection.
var stock = db.StockItems.Find(4);
var stores = stock.StockGroups.Select(g => g.Stores);
How can I rewrite this to combine the StockGroups to give me a collection of Stores?
Thank you!

You can use SelectMany() to flatten the hierarchy:
var stores = stock.SelectMany(s => s.StockGroups).SelectMany(g => g.Stores).ToList();
http://blogs.interknowlogy.com/2008/10/10/use-linqs-selectmany-method-to-flatten-collections/

Related

One-to-Many relations in RESTful api using Web Api 2

I have a question about setting up Web Api 2 so that if there is a relationship between two entities, at least querying one of those includes the other entities in the result. It's hard to explain but very easy concept if you look at the example below:
So, Imagine here is what we have in the Entity Framework:
public class student {
public int Id {get; set;}
public string Name {get; set;}
public int School_Id {get;set;}
public virtual School School {get; set;}
}
And:
public class school {
public int Id {get; set;}
public string Name {get; set;}
public IEnumerable<Student> Students {get; set;}
}
So obviously this is a very simple example. It's clear that these two classes can cause a circular reference, is there an easy way for me to generate json output that from one side, includes the related property and from the other side it doesn't? (to prevent the circular reference), to make it clear, when I query schools, I want to get this:
[{Id:1, Name: "School A", Students: [{Id:1, Name:"Mike"}, {Id:2, Name: "Sheila"}]} ,
{Id:2, Name: "School B", Students: [{Id:3, Name:"Joe"}, {Id:4, Name: "Sarah"}]}]
And when I query students, I only get this (see there is no school):
[{Id:1, Name:"Mike", School_Id:1}, {Id:2, Name:"Sheila", School_Id:1}, {Id:3, Name:"Joe", School_Id:2}, {Id:4, Name:"Sarah", School_Id:2}]
There are a few ways to handle this situation, and the method you choose depends on a few factors. I'll list the common ways this is normally dealt with, and you can evaluate each to decide which makes sense for both your client and server configuration.
Use a View Model or anonymous projection. This method involves creating a unique class which has only the properties you wish to send to the client.
public class StudentVm {
public int Id {get; set;}
public string Name {get; set;}
public string SchoolName {get;set;}
}
Use the [JsonIgnore] attribute on properties you do not want to have JSON serialize. If you prefer the Opt-In approach, use [DataMember] on properties you wish to include in serialization.
public class student {
public int Id {get; set;}
public string Name {get; set;}
public int School_Id {get;set;}
[JsonIgnore]
public virtual School School {get; set;} //omitted
}
public class student {
[DataMember]
public int Id {get; set;}
[DataMember]
public string Name {get; set;}
[DataMember]
public int School_Id {get;set;}
public virtual School School {get; set;} //omitted by default
}
Use the initializer for the JSON Formatter to preserve references. A unique $id field will be added to each object, and any object which may cause a circular reference will be replaced with a $ref pointing to that object's $id value.
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
[{"$id":"1", "Id":"1", "Name":"Mike", "School_Id":"1",
{"$id":"2", "Id":"1", "Name": "School A", "Students": [{"$ref":"1"}]
}]
Note that using a combination of these methods together is acceptable; You could, for example, use a View Model and use References Handling together.
References from http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
Could you explicitly create objects in the form you want?
Schools.Select(s => new
{
s.Id,
s.Name,
Students = s.Students.Select(st => new {st.Id, st.Name})
})
Students.Select(st => new { st.Id, st.Name, st.School_Id })

How to create a Join using petapoco MVC4

I need to join 2 table using petapoco MVC4
my table cong shown below
[TableName("District")]
[PrimaryKey("nDistrictID")]
public class District
{
public int nDistrictID { get; set; }
public string cDistrictName { get; set; }
public bool bActive { get; set; }
public int nStateID { get; set; }
}
And state table
[PetaPoco.TableName("States")]
[PetaPoco.PrimaryKey("nStateID")]
public class States
{
public int nStateID { get; set; }
public string cStateName { get; set; }
}
I need a query in this form
select d.cDistrictName,s.cStateName, d.nStateID from District
d inner join States s on d.nStateID=s.nStateID
I am assuming you have already a dataContext which contains 2 DbSet of each classes. Regarding that you can use:
Regarding the result you expect, you can create a new class which matches with the result:
public class DistrictWithState
{
public string cDistrictName {get;set;}
public string cStateName {get; set;}
public int nStateId {get;set;}
}
Then in your Action:
var dataContext = new PetaPoco.Database("mysql");
var sql="select d.cDistrictName,s.cStateName, d.nStateID from District
d inner join States s on d.nStateID=s.nStateID";
var districts = db.Fetch<DistrictWithState>(sql);
return view(districts);
There is another solution using the dynamic keyword. But just start with that solution above. It should work. I hope it will help

How can we manage the Relation between two table in POCO Entity?

I am a newbie to POCO.I have two tables like tb1 and tb2.Suppose we have a PK and FK relation between these tables.When it come to POCO CF how can we manage this relations?I have a done a sample by following a article.
public abstract class Person
{
public string Name { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
}
public class Collaborator : Person
{
public int CollaboratorId { get; set; }
public string ManagerCode { get; set; }
public virtual Manager Manager { get; set; }
}
Why they have used the abstract and virtual keywords? Can any one explain me the how can we manage the relations?
I assume you are using a model-first approach. You will want to use the Fluent API to define the relationships. Here is a good article on how to do this.

Working with a Model class that has a foreign/navigation key to itself

I am trying to develop a catalog project in ASP.NET MVC 3 and using EF Code first with an existing Database. There is a Categories table in my database that points to itself. For that, I have written the following model class. --"Correct me if the model is wrong"--
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public int? ParentCategoryID { get; set; }
public string CategoryDesc { get; set; }
[ForeignKey("ParentCategoryID")]
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Question : I am unable to understand as to how can i work with this class. While using and passing the following code to the view
var cat = dbStore.Categories.Include("ParentCategory").ToList().
I got this error : Object reference not set to an instance of an object. This is happening because the root category has null ParentCategoryID. Please tell me how will you work with this code or any resource that can help me understand working in such scenarios. Just any sort of code will be helpful that uses the above the model, like displaying a list or a menu or anything, just anything.
Usually what you do is travel from top level categories to bottom level categories. Inorder to do that first you need to define SubCategories collection in your class
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public int? ParentCategoryID { get; set; }
public string CategoryDesc { get; set; }
[ForeignKey("ParentCategoryID")]
public virtual Category ParentCategory { get; set; }
[InverseProperty("ParentCategory")]
public virtual ICollection<Category> SubCategories{ get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Then you retrieve the top level categories
var topCategories = dbStore.Categories
.Where(category => category.ParentCategoryID == null)
.Include(category => category.SubCategories).ToList();
After that you can traverse the hierachey
foreach(var topCategory in topCategories)
{
//use top category
foreach(var subCategory in topCategory.SubCategories)
{
}
}
If you do not have very many categories you can solve this by loading the whole collection of categories. I think EF will handle the fixup for you so all relations are properly populated.
As far as I know there are no SQL'ish databases/ORM's that can handle this scenario well. An approach I often use is to load the whole collection as I said above and then manually fix the relations. But I do think EF will do that for you.
Basically you should do:
var topCategories = dbStore.Categories.ToList().Where(category => category.ParentCategoryID == null);

EF 4.1 CF Fluent API mapping problemo

I'm new to the fluent API. I have a legacy database which I can't alter at the moment. Simply, this is what I need to achieve:
public class ItemCategory
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<ItemCategory> ItemCategories { get; set; }
public virtual ICollection<Item> RelatedItems { get; set; }
}
Items can be in many categories, RelatedItems can be in different categories to the current Item (which may not have any related items), the existing join tables look like this:
ItemCategoriesItems (ID,ItemCategoryID,ItemID)
RelatedItemCategoriesItems (ID,ItemCategoriesItemsID,RelatedItemCategoriesItemsID)
Hopefully it's obvious that the related items join table above contains 2 foreign keys to the item categories join table - one pointing to the current item and the other to the related item. Currently my onModelCreating code has:
modelBuilder.Entity<ItemCategory>()
.HasMany(c => c.Items)
.WithMany(set => set.ItemCategories)
.Map(mc =>
{
mc.ToTable("ItemCategoriesItems","testdb");
mc.MapLeftKey("ItemCategoryID");
mc.MapRightKey("ItemID");
});
... which gets the categories/items working but I'm stuck on how to get the RelatedItems.
Any help greatly appreciated!