Select child record in relation two tables - entity-framework

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");

Related

EF Select Grandparent for Grandchildren

Class Brands, Models, Generations, Modification
How to get all Brands for ModificationName == "ACK"
public class Brand
{
public Brand()
{
this.Models = new HashSet<Model>();
}
public int BrandId { get; set; }
public virtual ICollection<Model> Models { get; set; }
}
public class Model
{
public Model()
{
this.Generations = new HashSet<Generation>();
}
public virtual ICollection<Generation> Generations { get; set; }
public int? BrandId { get; set; }
public virtual Brand Brand { get; set; }
}
public class Generation
{
public Generation()
{
this.Modifications = new HashSet<Modification>();
}
public int GenerationId { get; set; }
public virtual ICollection<Modification> Modifications { get; set; }
public int? ModelId { get; set; }
public virtual Model Model { get; set; }
}
public class Modification
{
public int ModificationId { get; set; }
public string ModificationName { get; set; }
public int? GenerationId { get; set; }
public virtual Generation Generation { get; set; }
}
Another approach could be to use the Join operator. For example>
from currentBrand in Context.Brand
join currentModel in context.Model on currentBrand.Id equals currentModel.BrandId
join currentGeneration in context.Generations on currentGeneration.ModelId equals currentModel.id
join currentModeification in context.Modification on currentModeification.GenerationId equals currentGeneration .Id
Where currentModeification.ModificationName == "ACK"
Trick here is to use SelectMany method.
var query =
from b in ctx.Brands
where b.Models
.SelectMany(m => m.Generations.SelectMany(g => g.Modifications))
.Where(m => m.ModificationName == "ACK").Any()
select b;
UPDATE with includes
It will work only with EF Core 5
var query =
from b in ctx.Brands
.Include(b => b.Models)
.ThenInclude(g => g.Generations)
.ThenInclude(m => m.Modifications.Where(x => x.ModificationName == "ACK"))
where b.Models
.SelectMany(m => m.Generations.SelectMany(g => g.Modifications))
.Where(m => m.ModificationName == "ACK").Any()
select b;

Defining the one to many relationship in OnModelCreating using Entity Framework Core 3.1

I am new to Entity Framework Core 3.1 and trying to define the one-to-many relationship between two tables. I am currently struggling and getting compilation errors. Could somebody tell me what the problem could be.
The error is:
PersonNote does not contain the definition for PersonNote
I am currently getting is at line
entity.HasOne(d => d.PersonNote)
How else could I define one-to-many relationship?
The two tables are Person and PersonNote. One Person can have many PersonNotes. I have defined the models for them
public class Person
{
public int Id { get; set; }
public int? TitleId { get; set; }
public string FirstName { get; set; }
public string FirstNamePref { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime? DateOfBirth { get; set; }
public string Gender { get; set; }
public int AddressId { get; set; }
public string TelephoneNumber { get; set; }
public string MobileNumber { get; set; }
public string Email { get; set; }
public int? PartnerId { get; set; }
public bool Enabled { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
public string ModifiedBy { get; set; }
public DateTime Modified { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDateTime { get; set; }
public Address Address { get; set; }
public Title Title { get; set; }
public Client Client { get; set; }
internal static IEnumerable<object> Include(Func<object, object> p)
{
throw new NotImplementedException();
}
public PersonNote PersonNote { get; set; }
}
public class PersonNote
{
public int Id { get; set; }
public int PersonId { get; set; }
public string Note { get; set; }
public int AuthorId { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDateTime { get; set; }
}
public IEnumerable<PersonNote> GetPersonNotes(int personId)
{
var PersonNotes = PersonNote
.Include(x => x.)
.Where(x => x.Id == personId)
.ToList();
return PersonNotes;
}
I have tried the following in OnModelCreating:
modelBuilder.Entity<PersonNote>(entity =>
{
entity.ToTable("PersonNote", "common");
entity.HasOne(d => d.PersonNote)
.WithMany(p => p.Person)
.HasForeignKey(d => d.PersonId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_commonPersonNote_commonPerson");
});
You should have have something like this (other properties are omitted):
class Person
{
[Key]
public int Id { get; set; }
public List<PersonNote> PersonNotes { get; set; }
}
class PersonNote
{
[Key]
public int Id { get; set; }
public int PersonId { get; set; }
}
class StackOverflow : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<PersonNote> PersonNotes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(p => p.PersonNotes)
.WithOne()
.HasForeignKey(p => p.PersonId);
}
}

Entity Framework Core self referencing table

I have a class called Item which references the next item and the previous item.
public class Item
{
private Item() { }
public Item(string itemName)
{
ItemId = Guid.NewGuid();
ItemName = itemName;
}
public Guid ItemId { get; set; }
public string ItemName { get; set; }
public Guid NextItemId { get; set; }
public virtual Item NextItem { get; set; }
public Guid PreviousItemId { get; set; }
public virtual Item PreviousItem { get; set; }
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
}
I have another table called Group it is for grouping it items.
public class Group
{
private Group() { }
public Group(string groupName)
{
GroupId = Guid.NewGuid();
GroupName = groupName;
GroupItems = new List<Item>();
}
public void AddGroupItem(Item item)
{
if (Items.Count == 0)
{
Items.Add(item);
}
else
{
item.PreviousItem = Items.Last();
item.PreviousItemId = Items.Last().ItemId;
Items.Last().NextItem = item;
Items.Last().NextItemId = item.ItemId;
Items.Add(item);
}
}
public Guid GroupId { get; set; }
public string GroupName { get; set; }
public virtual IList<GroupItem> GroupItems { get; set; }
}
Here's how I create and save items and their group.
Group group1 = new Group("first group");
Item item1 = new Item("item 1");
Item item2 = new Item("item 2");
Item item3 = new Item("item 3");
group1.AddItem(item1);
group1.AddItem(item2);
group1.AddItem(item3);
_context.Add(group1);
_context.SaveChanges();
How do I write the OnModelCreating to handle the two references to the same table.
You can do it in next way. First of all you should add two new properties to you model public virtual List<Item> ParentNextItems { get; set; } and public virtual List<Item> ParentPreviousItems { get; set; }. So your model will be something like this
public class Item
{
private Item() { }
public Item(string itemName)
{
ItemId = Guid.NewGuid();
ItemName = itemName;
}
public Guid ItemId { get; set; }
public string ItemName { get; set; }
public Guid? NextItemId { get; set; }
public virtual Item NextItem { get; set; }
public virtual List<Item> ParentNextItems { get; set; }
public Guid? PreviousItemId { get; set; }
public virtual Item PreviousItem { get; set; }
public virtual List<Item> ParentPreviousItems { get; set; }
}
And than you can configure it in next way
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>()
.HasKey(x => x.ItemId);
modelBuilder.Entity<Item>()
.HasOne(x => x.NextItem).WithMany(x => x.ParentNextItems).HasForeignKey(x => x.NextItemId)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;
modelBuilder.Entity<Item>()
.HasOne(x => x.PreviousItem).WithMany(x => x.ParentPreviousItems).HasForeignKey(x => x.PreviousItemId)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;
base.OnModelCreating(modelBuilder);
}
That's all. But if you want to achieve the same with attribute configuration, than you can skip void OnModelCreating(ModelBuilder modelBuilder) changes and just write next model:
public class Item
{
private Item() { }
public Item(string itemName)
{
ItemId = Guid.NewGuid();
ItemName = itemName;
}
[Key]
public Guid ItemId { get; set; }
public string ItemName { get; set; }
public Guid? NextItemId { get; set; }
[ForeignKey(nameof(NextItemId))]
[InverseProperty(nameof(ParentNextItems))]
public virtual Item NextItem { get; set; }
[ForeignKey(nameof(NextItemId))]
public virtual List<Item> ParentNextItems { get; set; }
public Guid? PreviousItemId { get; set; }
[ForeignKey(nameof(PreviousItemId))]
[InverseProperty(nameof(ParentPreviousItems))]
public virtual Item PreviousItem { get; set; }
[ForeignKey(nameof(PreviousItemId))]
public virtual List<Item> ParentPreviousItems { get; set; }
}
Update
Also you should make PreviousItemId, NextItemId optional (Guid?), I did corresponding changes in my answer.
And as far as I know, you just can't do it in one .SaveChanges() trip. When you create your second item you should already have first item saved into database. (anyway this is subject for another question)
But anyway if you modify you code to something like that
Group group1 = new Group("first group");
_context.Add(group1);
Item item1 = new Item("item 1");
group1.AddItem(item1);
_context.SaveChanges();
Item item2 = new Item("item 2");
group1.AddItem(item2);
_context.SaveChanges();
Item item3 = new Item("item 3");
group1.AddItem(item3);
_context.SaveChanges();
You can do it in one transaction
You don't need to have Collection, since as far I understand here you aim to create some type of Two Way LinkedList. A was able to fix it by adding .HasOne() to the main model.
modelBuilder.Entity<Item>().HasOne(x => x.NextItem);
modelBuilder.Entity<Item>().HasOne(x => x.PreviousItem);
public int? DeaultNextStateId { get; set; }
public State DeaultNextState { get; set; }
public virtual ICollection<State> DeaultNextStates { get; set; }
public int? OkNextStateId { get; set; }
public State OkNextState { get; set; }
public virtual ICollection<State> OkNextStates { get; set; }
public int? NotOkNextStateId { get; set; }
public State NotOkNextState { get; set; }
public virtual ICollection<State> NotOkNextStates { get; set; }
modelBuilder.Entity<Models.Workflow.State>()
.HasKey(x => x.Id);
modelBuilder.Entity<Models.Workflow.State>()
.HasOne(x => x.DeaultNextState).WithMany(x => x.DeaultNextStates).HasForeignKey(x => x.DeaultNextStateId)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;
modelBuilder.Entity<Models.Workflow.State>()
.HasOne(x => x.OkNextState).WithMany(x => x.OkNextStates).HasForeignKey(x => x.OkNextStateId)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;
modelBuilder.Entity<Models.Workflow.State>()
.HasOne(x => x.NotOkNextState).WithMany(x => x.NotOkNextStates).HasForeignKey(x => x.NotOkNextStateId)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;

How to combine multiple table using Linq?

public class JobDescription
{
public int JobDescriptionID { get; set; }
//
public virtual List<Image> Image { get; set; }
}
public class Image
{
public int ImageID { get; set; }
[Required]
public int JobDescriptionID { get; set; }
[ForeignKey("JobDescriptionID")]
public virtual JobDescription JobDescription { get; set; }
public virtual List<ImageSection> ImageSection { get; set; }
}
public class ImageSection
{
public int ImageSectionID { get; set; }
//
public int ImageID { get; set; }
[ForeignKey("ImageID")]
public virtual Image Image { get; set; }
public virtual DigitalSection DigitalSection { get; set; }
}
public class DigitalSection
{
public int DigitalSectionID { get; set; }
public int ImageSectionID { get; set; }
[ForeignKey("ImageSectionID")]
public virtual ImageSection ImageSection { get; set; }
public virtual VerifiedSection VerifiedSection { get; set; }
}
public class VerifiedSection
{
public int VerifiedSectionID { get; set; }
public string DigitizedText { get; set; }
public int DigitalSectionID { get; set; }
[ForeignKey("DigitalSectionID")]
public virtual DigitalSection DigitalSection { get; set; }
}
I am using CodeFirst approach and I have JobDscriptionID. Now i want to retireve all the DigitizedText from VerifiedSection Table. How to do it ?
Try this :
var result = contetx.VerifiedSection
.Where(V => V.DigitalSection.ImageSection.Image.JobDescription.JobDescriptionID == 1)
.Select(V => V.DigitizedText);
Alternately you could also use Join
var result = context.VerifiedSection.Join(context.DigitalSection.Join(
(context.ImageSection.Join
(context.Image.Join
(context.JobDescription.Where(J=> .JobDescriptionID == 1)), I=> I.JobDescriptionID, J => J.JobDescriptionID , (I,J) => I)
IS => IS.ImageID, I=> I.ImageID, (IS,I) => IS)
D => D.ImageSectionID, IS => IS.ImageSectionID , (D,IS) => D)
V => V.DigitalSectionID, D => D.DigitalSectionID, (V,D) => V.DigitizedText);
Good Luck !!
var query = (from image in Context.Image
Where image.JobDescriptionID == id
select image.ImageID).SingleOrDefault();
var query2 = (from select in Context.ImageSection
Where select.ImageID == query
select select.ImageSectionID).SingleOrDefault();
var query3 = (from digital in Context.DigitalSection
Where digital.ImageSectionID == query2
select digital.DigitalSectionID).SingleOrDefault();
var query4 = from text in Context.VerifiedSection
Where text.VerifiedSection == query3
select select.DigitizedText;
Kundan Singh Chouhan gave you a better answer, but perhaps you'd like to do it the "Queries" way.

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.