EF6 Self Referencing Table with multiple parent properties, single child collection - entity-framework

I have a table that references itself, but I am struggling with getting my desired mapping. I want to be able to define Children to be a collection of people that have a given person as Mother, Father, and/or Guardian. A Guardian may be the father or mother.
I am wanting to have a tree view of people that is browsable where people are listed; the user can expand a person's node to show all that person's children, regardless off the child-defining relationship (Mother, Father, or Guardian).
public partial class Person
{
[Key]
public int ID { get; set; }
[StringLength(100)]
public string Name { get; set; }
public int? GuardianID { get; set; }
[Column("MotherID")]
public int? MotherID { get; set; }
[Column("FatherID")]
public int? FatherID { get; set; }
[ForeignKey("MotherID")]
public virtual tblPerson Mother { get; set; }
[ForeignKey("FatherID")]
public virtual tblPerson Father { get; set; }
[ForeignKey("GuardianID")]
public virtual tblPerson Guardian { get; set; }
[InverseProperty("Guardian")]
[InverseProperty("Father")]
[InverseProperty("Mother")]
public virtual IEnumerable<tblPerson> children { get; set; }
}
Any help would be appreciated right now my view has to look like this:
#using Person_MVC.Models
#model IEnumerable<Person>
#{
IEnumerable<Person> children;
}
<ul>
#foreach (Person person in Model.OrderBy(p => p.PersonNumber))
{
<li id="Pnl_#Person.ID" data-jstree='{"type":"Person"}' data-Personkey="#Person.ID.ToString()">
#Person.Name
#{
PersonModel db = new PersonModel();
children = (from p in db.Persons where p.GuardianID == Person.ID || p.Father == Person.ID || p.MotherID == Person.ID select p).ToList();
}
#if (children != null && children.Count() > 0)
{
#Html.Partial("PersonTree", children)
}
</li>
}
</ul>

I guess the better solution is to make three navigation lists in your model and may have one method to join the objects to return all sons to you.
e.g.
public int? FatherId { get; set; }
public int? GrandFatherId { get; set; }
public int? MotherId { get; set; }
public virtual ICollection<Person> FatherForThose { get; set; }
public virtual Person Father { get; set; }
public virtual ICollection<Person> GrandFatherForThose { get; set; }
public virtual Person GrandFather { get; set; }
public virtual ICollection<Person> MotherForThose { get; set; }
public virtual Person Mother { get; set; }
public ICollection<Person> GetChildren()
{
var list = FatherForThose.Concat(MotherForThose).ToList();
foreach (var person in GrandFatherForThose)
{
if (list.All(i => i.Id != person.Id))
{
list.Add(person);
}
}
return list;
}
but you should always take care to include them in your querying
e.g.
var grand = context.Persons.Include(x => x.FatherForThose)
.Include(x => x.GrandFatherForThose)
.Include(x => x.MotherForThose)
.FirstOrDefault(x => x.Id == 2);
var list = grand.GetChildren();

All the table data should be in hand (If not we might have multiple calls to database).
Find list of all the Persons who doesn't have parents(i.e; no guardianid, motherid, parentid for a person) and start the partial with them.

Try can this too...
public partial class Person
{
[Key]
public int ID { get; set; }
[StringLength(100)]
public string Name { get; set; }
public int? GuardianID { get; set; }
[Column("MotherID")]
public int? MotherID { get; set; }
[Column("FatherID")]
public int? FatherID { get; set; }
public IEnumerable<Person> Children { get
{
return context.Person.Where(p => p.GuardianID == this.ID || p.Father == this.ID || p.MotherID == this.ID).ToList();
}
}
}
#using Person_MVC.Models
#model IEnumerable<Person>
<ul>
#foreach (Person person in Model.OrderBy(p => p.PersonNumber))
{
<li id="Pnl_#Person.ID" data-jstree='{"type":"Person"}' data-Personkey="#Person.ID.ToString()">
#Person.Name
#if (Person.Children != null && Person.Children.Count() > 0)
{
#Html.Partial("PersonTree", Person.Children)
}
</li>
}
</ul>

Related

Entity Framework 6 (code first) using child collection foreign key without the parents primary key

I really think I am missing something here that's probably really simple that's not jumping out at me.
I have these objects and I am trying to join a parent object to a child collection but not necessarily using the parent's primary key. In sql I can do this pretty easily, but it's bugging me why this cannot happen using code first. I am trying to join CompetitorMatchInformation to BrandSkuPricing by the ErpSkuId.
public class CompetitorMatchInformation {
[Key(), Column("MatchId")]
public long MatchId { get; set; }
[Column("ErpSkuId")]
public int? ErpSkuId { get; set; }
[Column("CompetitorId")]
public int CompetitorId { get; set; }
[ForeignKey("CompetitorId")]
public virtual Competitors Competitor { get; set; }
[ForeignKey("CompetitorItemToErpSkuMatchId")]
//[ForeignKey("ErpSkuId")]
public virtual List<BrandSkuPricing> BrandSkuPricing { get; set; }
}
public class Competitors
{
[Key(), Column("CompetitorId")]
public int CompetitorId { get; set; }
[Column("CompetitorName")]
public string CompetitorName { get; set; }
}
public class BrandSkuPricing
{
[Key(), Column("BrandSkuId")]
public int BrandSkuId { get; set; }
[Column("CompetitorItemToErpSkuMatchId")]
public long CompetitorItemToErpSkuMatchId { get; set; }
[Column("ErpSkuId")]
public int? ErpSkuId { get; set; }
[Column("Price")]
public decimal? Price { get; set; }
[Column("BrandId")]
public int? BrandId { get; set; }
[Column("BrandSourceSytemId")]
public string BrandSourceSytemId { get; set; }
[Column("BrandName")]
public string BrandName { get; set; }
[Column("BrandSkuNumber")]
public string BrandSkuNumber { get; set; }
}
The Competitor comes over correctly, but the child collection not so much. This isn't a normal scenario I know, but the underlying view for BrandSkuPricing has a relationship that's not entirely normal.
The query I am using is
public List<CompetitorMatchInformation> GetCompetitorMatchInfoByCompetitorItemId(long competitorItemId, int? brandId = null)
{
var query = this.Entity.Include(x => x.CurrentChallenges).Include(x => x.BrandSkuPricing);
var list = query.Where(x => x.CompetitorItemId == competitorItemId &&
((x.CurrentChallenges.Count > 0 && x.CurrentChallenges.Any(w => !w.IsResolved)) ||
x.CurrentChallenges.Count == 0))
.ToList();
list.ForEach(l =>
{
if (brandId.HasValue)
{
l.BrandSkuPricing = l.BrandSkuPricing.Where(x => x.BrandId == brandId).ToList();
}
});
return list;
}
And in the model builder, I have nothing. I have tried but cannot get it to work even in the builder. Anyway I can get the child collection to join on ErpSkuId? I have changed the underlying view to pull in the CompetitorItemToErpSkuMatchId so it working that way, but this scenario of joining on something that isn't a key will come up for me a lot soon.
Thanks!

Select child record in relation two tables

What is the best solutions to retrieve records from child table in relation?
I cannot include the solution file in this question.
Model
[Table("Tbl_DefaultValue")]
public class DefaultValue
{
[Key]
public int DefaultValue_ID { get; set; }
public string DefaultVal_Name { get; set; }
public virtual ICollection<DefaultValue_Det> DefaultValue_Det { get; set; }
}
[Table("Tbl_DefaultValue_Det")]
public class DefaultValue_Det
{
[Key]
public int DefaultValue_Det_ID { get; set; }
public int DefaultValue_ID { get; set; }
public string DefaultValue_Value { get; set; }
public virtual DefaultValue DefaultValue { get; set; }
public virtual ICollection<Car> Cars { get; set; }
}
Controller
driverdt.TypeList =
new SelectList(db.DefaultValue_Det
.Where(a => a.DefaultValue_ID == db.DefaultValue
.Where(d => d.DefaultVal_Name == "marid")
.Max(b=>b.DefaultValue_ID)), "DefaultValue_Det_ID", "DefaultValue_Value");
return View( driverdt);
You can pre-populate collection DefaultValue_Det using FetchMany:
driverdt.TypeList =
new SelectList(db.DefaultValue_Det
.Where(a => a.DefaultValue_ID == db.DefaultValue
.Where(d => d.DefaultVal_Name == "marid")
.Max(b=>b.DefaultValue_ID))
.FetchMany(x => x.DefaultValue_Det)
, "DefaultValue_Det_ID", "DefaultValue_Value");

Entity Framework query returns one record, but its supposed to return more

As title say, i got problem with a query in Asp.net mvc 3 EF.
I got 2 tables with 1 to many relationship.
table1 Users int user_ID string username
table2 Friends int friendshipID int user_ID int friend_ID
The controller:
// // GET: /User/Details/5
public ViewResult Details(int id)
{
User user = db.Users.Include("Friends").FirstOrDefault(u => u.user_ID == id);
//Also for each friend get the User:
foreach (var friend in user.Friends.ToList())
{
friend.User = db.Users.Find(friend.friend_ID);
}
return View(user);
}
The view "details":
#model Social2.Models.User
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div class="display-field">
#foreach (var friend in #Model.Friends)
{
#friend.User.username;
}
</div>
Context:
public partial class ASPNETDBEntities : DbContext
{
public ASPNETDBEntities()
: base("name=ASPNETDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<aspnet_Users> aspnet_Users { get; set; }
public DbSet<Friend> Friends { get; set; }
public DbSet<User> Users { get; set; }
}
user model:
public partial class User
{
public User()
{
this.Friends = new HashSet<Friend>();
}
[Key]
public int user_ID { get; set; }
public System.Guid user_UniqueID { get; set; }
public string username { get; set; }
public virtual aspnet_Users aspnet_Users { get; set; }
public virtual ICollection<Friend> Friends { get; set; }
}
friend model
public partial class Friend
{
public int friendship_ID { get; set; }
public int user_fr_ID { get; set; }
public int friend_ID { get; set; }
public virtual User User { get; set; }
}
The problem is, when i go to ~/user/details/1, the view show only one(the last one) friend.For every user it shows their last friend. How to show them all ?
Your database must have two relationships defined like so:
If you create an Entity Model from this schema you also get two one-to-many relationships. And when you apply the DBContext T4 template to this model you should get POCO classes like so:
public partial class Users
{
public Users()
{
this.Friends = new HashSet<Friends>();
this.Friends1 = new HashSet<Friends>();
}
public int user_ID { get; set; }
public string username { get; set; }
public virtual ICollection<Friends> Friends { get; set; }
public virtual ICollection<Friends> Friends1 { get; set; }
}
public partial class Friends
{
public int friendship_ID { get; set; }
public int user_fr_ID { get; set; }
public int friend_ID { get; set; }
public virtual Users Users { get; set; }
public virtual Users Users1 { get; set; }
}
Users.Friends and Friends.Users form a pair for the first relationship and Users.Friends1 and Friends.Users1 are a pair for the second relationship. (You can rename the navigation properties in the model designer to make the names more meaningful.) Your query would look like this then (you can include a "second level" and don't need the loop as you did in your example):
public ViewResult Details(int id)
{
// important to use User1 in Include, not User
User user = db.Users.Include("Friends.User1")
.FirstOrDefault(u => u.user_ID == id);
return View(user);
}
With DbContext you can also use the strongly typed version of Include:
Include(u => u.Friends.Select(f => f.User1))
I think the problem is mapping of the class Friend.
try change to:
public partial class Friend
{
[Key]
public int friendship_ID { get; set; }
public int user_fr_ID { get; set; }
public int friend_ID { get; set; }
[ForeignKey("friend_ID")]
public virtual User User { get; set; }
}
I think the problem is here
User user = db.Users.Include("Friends").FirstOrDefault(u => u.user_ID == id);
FirstOrDefault gives only one record.

Filtering related entities with Entity framework

I'm looking for the best way to to load and filter related child entities. I have something that works, but I'm unsure if it's the best or even the right way to achieve what I want. Working code example below. Pros and cons would be great! Thanks!
public Site Find(int siteID)
{
// Can't use include here, not possible to filter related (child) entities
// return _context.Sites.Where(x => x.ID == siteID)
// .Include("SiteLoggers")
// .Where(x => x.Deleted == false)
// .FirstOrDefault();
var site = _context.Sites.Where(x => x.ID == siteID).FirstOrDefault();
if(site != null)
{
site.SiteLoggers = site.SiteLoggers.Where(x => x.SiteID == siteID &&
x.Deleted == false)
.ToList();
}
return site;
}
EDIT:
Added POCOS
public class Site
{
public int ID { get; set; }
public int LocationID { get; set; }
public bool Active { get; set; }
public bool Deleted { get; set; }
public string Name { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<SiteLogger> SiteLoggers { get; set; }
public virtual ICollection<LinkDcSite> DcSiteLinks { get; set; }
}
public class SiteLogger
{
public int ID { get; set; }
public int UID { get; set; }
public int SiteID { get; set; }
public int LocationID { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public bool Deleted { get; set; }
public virtual Site Site { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<SiteLoggerSensor> SiteLoggerSensors { get; set; }
public virtual ICollection<LinkLoggerSiteLogger> LinkLoggerSiteLogger { get; set; }
}
Your method is fine I think you have just extra checking for x.SiteID == siteID:
....
site.SiteLoggers = site.SiteLoggers.Where(x => !x.Deleted).ToList();
....
Also if you searching by ID means you are sure there is no two element with same ID, so it's better to use SingleOrDefault instead of FirstOrDefault, to throw an exception in the case there are more than one item with one ID.
var site = _context.Sites.Where(x => x.ID == siteID).SingleOrDefault();
You can do that with a simple query:
var site = _context.SiteLoggers.Where(sl => sl.SiteId = siteId && !sl.Deleted).ToList();
If there's a relation between SiteLoggers and Sites, you don't need to chek that the site exists.

EF code-first: How to load related data (parent-child-grandchild)?

I have this entity:
public class DynamicPage {
public int PageId { get; set; }
public int Order { get; set; }
public string MenuText { get; set; }
public string MenuHover { get; set; }
public int? ParentId { get; set; }
public virtual DynamicPage Parent { get; set; }
public virtual ICollection<DynamicPage> Children { get; set; }
}
This entity may have 3 level: Parent -> Child -> Grandchild. How can I load the Parent (level 1) whit all associated children (level 2) and for each child, associated grandchild (level 3) if any? Thanks to help.
EF 4.1 feature and syntax:
var entity = context.Parents
.Include(p => p.Children.Select(c => c.GrandChildren))
.FirstOrDefault(p => p.Id == 1); // or whatever condition
If you want to make life easy on yourself, follow the EF Code First conventions of naming your table IDs simply Id (or, alternatively, name of table + Id, e.g., DyanmicPageId).
This should leave you with something like this:
public class DynamicPage
{
public int Id { get; set; }
public int Order { get; set; }
public string MenuText { get; set; }
public string MenuHover { get; set; }
public int? ParentId { get; set; }
public virtual DynamicPage Parent { get; set; }
public virtual ICollection<DynamicPage> Children { get; set; }
}
Then you need to set up the relationship between parents and children explicitly in an OnModelCreating method in your DbContext class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DynamicPage>()
.HasMany(page => page.Children)
.WithRequired(child => child.Parent)
.HasForeignKey(child => child.ParentId);
}
You can then select children or grandchildren as needed:
var parent = dbContext.DynamicPages.Where(page => page.ParentId == null);
var children = parent.Children;
var grandchildren = parent.SelectMany(page => page.Children);
var allRelatedPages = parent.Union(children).Union(grandchildren);