EF Select Grandparent for Grandchildren - entity-framework

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;

Related

Json response does not contain all the navigation properties EntityFramework Core and ASP .NETCore Web API

I have migrated from Entity Framework 6 to EF Core and also Web Api .net framework to .net core.
I have many to many relationship that I have set up as follows
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var instrumentsToPlaces = modelBuilder.Entity<InstrumentPlace>();
instrumentsToPlaces.ToTable("InstrumentsToPlaces");
instrumentsToPlaces.HasKey(x => new { x.PlaceId, x.InstrumentId });
instrumentsToPlaces.HasOne(i => i.Instrument)
.WithMany(p => p.InstrumentsPlaces)
.HasForeignKey(ip => ip.InstrumentId);
instrumentsToPlaces.HasOne(p => p.Place)
.WithMany(i => i.InstrumentsPlaces)
.HasForeignKey(ip => ip.PlaceId);
var instrumentsToStyle = modelBuilder.Entity<InstrumentStyle>();
instrumentsToStyle.ToTable("InstrumentsToStyles");
instrumentsToStyle.HasKey(x => new { x.StyleId, x.InstrumentId });
instrumentsToStyle.HasOne(i => i.Instrument)
.WithMany(s => s.InstrumentStyles)
.HasForeignKey(si => si.InstrumentId);
instrumentsToStyle.HasOne(s => s.Style)
.WithMany(i => i.InstrumentStyles)
.HasForeignKey(si => si.StyleId);
}
I have included the navigation properties in the repository method as follows
public Instrument GetInstrumentByName(string name)
{
using (var starsAndCatzDbContext = new StarsAndCatzDbContext())
{
var instrument = _starsAndCatzDbContext.Instruments
.Include(a=>a.InstrumentsPlaces)
.ThenInclude(a=>a.Place)
.Include(a=>a.InstrumentStyles)
.ThenInclude(a=>a.Style)
.FirstOrDefault(i => i.Name == name);
return instrument;
}
}
Here are the classes
public class Instrument {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<InstrumentPlace> InstrumentsPlaces { get; set; }
public virtual ICollection<InstrumentStyle> InstrumentStyles { get; set; }
}
public class InstrumentPlace
{
public int InstrumentId { get; set; }
public Instrument Instrument { get; set; }
public int PlaceId { get; set; }
public Place Place { get; set; }
}
public class InstrumentStyle
{
public int InstrumentId { get; set; }
public Instrument Instrument { get; set; }
public int StyleId { get; set; }
public Style Style { get; set; }
}
public class Style {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<InstrumentStyle> InstrumentStyles { get; set; }
}
public class Place {
public int Id { get; set; }
public string Name { get; set; }
public string Division { get; set; }
public int Tier { get; set; }
public string State { get; set; }
public string Postcode { get; set; }
public float? Latitude { get; set; }
public float? Longitude { get; set; }
public virtual ICollection<InstrumentPlace> InstrumentsPlaces { get; set; }
}
The WebAPI method to be called is
[HttpGet("GetInstrumentByName/{suburb}/{instrument}"), Produces("application/json")]
public Instrument GetInstrumentByName(string suburb, string instrument)
{
try
{
var result = _instrumentRepository.GetInstrumentByName(instrument);
return result;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return new Instrument();
}
}
When I send the request to "/api/instruments/west-end/guitar" I get the expected result when I place a breakpoint before sending the response as follows
As you notice, the Navigation properties are loaded (when I expand the collections I can see all the properties being loaded as well).
However the json response I receive is the following
Any suggestions or am I missing something here?
Thank you all in advanced
Thanks #H. Herzl for giving me a hint.
The solution was found in this other question
services.AddMvc().AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
https://stackoverflow.com/a/40501464/1513346

Complex subquery in Entity Framework 6

I have an entity called Insurance like this:
public class Insurance : BaseEntity, IExpirationDocument
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; }
[Column(TypeName = "NVARCHAR")]
[StringLength(256)]
public string PathToCertificate { get; set; }
[Column(TypeName = "NVARCHAR")]
[StringLength(50)]
public string Filename { get; set; }
public int Value { get; set; }
public string Name => InsuranceType.Name;
public DateTime ExpiryDate { get; set; }
public DateTime IssueDate { get; set; }
public bool Active { get; set; }
public int InsuranceTypeId { get; set; }
public virtual InsuranceType InsuranceType { get; set; }
public int InsurerId { get; set; }
public virtual Insurer Insurer { get; set; }
public int ApplicantId { get; set; }
public virtual Applicant Applicant { get; set; }
public int? DocumentEmailHistoryId { get; set; }
public virtual DocumentEmailHistory DocumentEmailHistory { get; set; }
public Insurance()
{
Active = true;
}
}
Would it be possible to do this type of query with Entity Framework:
SELECT *
FROM Insurances i1
INNER JOIN
(SELECT
insuranceTypeId, applicantid, MAX(IssueDate) as 'maxissuedate'
FROM
Insurances
GROUP BY
insuranceTypeId, applicantid) AS i2 ON i1.applicantid = i2.applicantid
AND i1.insuranceTypeId = i2.insuranceTypeId
WHERE
i1.issueDate = i2.maxissuedate
If you are trying to get latest issued Insurance according to InsuranceTypeId and ApplicantId you can group data according to needed properties, order by IssueDate descendingly and take only one Insurance info. Of course it will not give you the same query but it will give you the same result:
var result = context.Insurances
.GroupBy(m => new { m.InsuranceTypeId , m.ApplicantId })
.Select( g => new
{
MaxInsurance = g.OrderByDescending(m => m.IssueDate)
.Take(1)
})
.SelectMany(m => m.MaxInsurance);

EF6 Ignoring related data

Scenario
public class Product : Entity, IAggregateRoot
{
public string Name { get; set; }
public string Dimension { get; set; }
public decimal Volume { get; set; }
public bool Featured { get; set; }
public Farm Farm { get; set; }
public int FarmId { get; set; }
/// <summary>
/// Sell Price
/// </summary>
public decimal BidPrice { get; set; }
public int QuantityAvaliable { get; set; }
public ICollection<Image> Images { get; set; }
public string Description { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
public DateTime Created { get; set; }
public DateTime? Modified { get; set; }
}
public class Category : Entity, IAggregateRoot
{
public string Title { get; set; }
public string CategoryImage { get; set; }
public Category Parent { get; set; }
public DateTime Created { get; set; }
public DateTime? Modified { get; set; }
}
Relationship setup
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
HasKey(x => x.Id);
Property(x => x.Created).HasColumnType("DateTime");
Property(x => x.Modified).HasColumnType("DateTime");
Property(x => x.BidPrice).HasColumnType("Decimal");
#region RELATIONSHIP
//BelongsTo
HasRequired(x => x.Farm);
HasRequired(x => x.Category);
HasMany(x => x.Images);
#endregion
}
So I have this two model where I need to bring the data from Product model with Category information
I have checked my database, the data is consistent, the Product record have the FK for the Category record.
but when I try to get Product Data using EF6, the category information doesnt come, I get a null object.
Because of = () =>
{
_product = _repository.Find(p => p.Id == 1, p => p.Category);
};
It should_not_be_bull = () =>
_product.Category.ShouldNotBeNull();
the response from data base is for Category is null. but the record is there.
I had it working properly before. for some random magic reason it just stop working.
THE FIND method
public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate = null, params Expression<Func<TEntity, object>>[] includes)
{
var set = CreateIncludedSet(includes);
return (predicate == null) ?
set.FirstOrDefault() :
set.FirstOrDefault(predicate);
}
the CreateIncludeSet
private IDbSet<TEntity> CreateIncludedSet(IEnumerable<Expression<Func<TEntity, object>>> includes)
{
var set = CreateSet();
if (includes != null)
{
foreach (var include in includes)
{
set.Include(include);
}
}
return set;
}
the CreateSet method
private IDbSet<TEntity> CreateSet()
{
return Context.CreateSet<TEntity>();
}
MY DbContext implementation is here
https://github.com/RobsonKarls/FreedomWebApi/blob/dev/Source/Freedom.Infrastructure.DataAccess/Factories/FreedomDbContext.cs
all project is there too for further analisys
any help is valuable.
Thank you
The problem in your code is in this line in CreateIncludedSet method:
set.Include(include);
Yes, you include the data but you do not change you set. You should change it to something like:
set = set.Include(include);
Your code is a bit unclear, but try something like this....
_product = _repository.Include(p => p.Category).SingleOrDefault(x => x.Id == 1);
also see...
https://stackoverflow.com/a/7348694/6200410

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

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.