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

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

Related

EF Core can't include children in parent

I'm quite new to EF Core.
In my DB Context:
// STEP
modelBuilder.Entity<CorsoStepS>().HasKey(x => new { x.codCorso, x.codStep });
modelBuilder.Entity<CorsoStepS>()
.HasMany(step => step.CorsoStepLezioni)
.WithOne(lez => lez.CorsoStep)
.HasForeignKey(lez => new { lez.codCorso, lez.codStep });
modelBuilder.Entity<CorsoStepS>().Ignore(step => step.CorsoStepLezioni);
////
// LEZIONI
modelBuilder.Entity<CorsoStepLezione>().HasKey(x => new { x.codCorso, x.codStep, x.codLezione });
modelBuilder.Entity<CorsoStepLezione>()
.HasMany(lez => lez.Sessioni)
.WithOne(sess => sess.Lezione)
.HasForeignKey(sess => new { sess.codCorso, sess.codStep, sess.codLezione });
modelBuilder.Entity<CorsoStepLezione>().Ignore(lez => lez.Sessioni);
////
// SESSIONI
modelBuilder.Entity<CorsoStepLezioneSessione>().HasKey(x => new { x.codCorso, x.codStep, x.codLezione, x.codSessione });
modelBuilder.Entity<CorsoStepLezioneSessione>()
.HasMany(sess => sess.Iscrizioni)
.WithOne(iscr => iscr.Sessione)
.HasForeignKey(iscr => new { iscr.CodCorso, iscr.CodStep, iscr.CodLezione, iscr.CodSessione });
modelBuilder.Entity<CorsoStepLezioneSessione>().Ignore(sess => sess.Iscrizioni);
////
My entities:
public class CorsoStepS
{
public int codCorso { get; set; }
public int codStep { get; set; }
public string nome { get; set; }
public int maxPartecipanti { get; set; }
public int order { get; set; }
public virtual Corso Corso { get; set; }
public virtual ICollection<CorsoStepLezione> CorsoStepLezioni { get; set; }
}
public class CorsoStepLezione
{
public int codCorso { get; set; }
public int codStep { get; set; }
public int codLezione { get; set; }
public string nome { get; set; }
public CorsoStepS CorsoStep { get; set; }
public virtual ICollection<CorsoStepLezioneSessione> Sessioni { get; set; }
}
public class CorsoStepLezioneSessione
{
public int codCorso { get; set; }
public int codStep { get; set; }
public int codLezione { get; set; }
public int codSessione { get; set; }
public DateTime? data { get; set; }
public string ora { get; set; }
public int maxPartecipanti { get; set; }
public virtual CorsoStepLezione Lezione { get; set; }
public virtual ICollection<CorsoStepLezioniSessioniIscrizione> Iscrizioni { get; set; }
}
When I call:
var lezioniCorso = _clienteContext.CorsoStepLezioni
.Include(lezione => lezione.Sessioni);
it gives me:
The expression 'lezione.Sessioni' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'.
But if I call:
var lezioniCorso = _clienteContext.CorsoStepLezioni
.Include(lezione => lezione.CorsoStep);
it's ok.
what am I doing wrong? I'm going stupid
It is because of this line
modelBuilder.Entity<CorsoStepLezione>().Ignore(lez => lez.Sessioni);
First you are telling EF to build up the relationship and then immediately ignore it again so EF acts as if this property does not exist.

Returning Entity with its children

Hi I am trying to return all vehicles with their recorded mileage through an api using ASP.Net Core with the following code:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.Include(m=>m.Mileages).ToList();
}
However this only returns the first vehicle with its mileages and not the others (there are five dummy vehicles in the db all with an initial mileage).
If I change the code to:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.ToList();
}
it returns the full list of vehicles but no mileage.
My class files are:
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<Mileage> Mileages { get; set; }
}
and
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
}
thanks for looking!
Tuppers
you can have them auto-load (lazy loading) using proxies... but for that, your foreign entities and collections must be marked virtual in your POCOs:
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public virtual ICollection<Mileage> Mileages { get; set; }
}
The proxy creation and lazy loading turned on, but that's the default in EF6.
https://msdn.microsoft.com/en-us/data/jj574232.aspx
Let me know if this works.
Well after a lot of searching I managed to find a solution. I used the following:
[HttpGet]
public IEnumerable<VehicleDto> Get()
{
var query = _context.Vehicles.Select(v => new VehicleDto
{
Registration = v.Registration,
Make = v.Make,
Model = v.Model,
Marked = v.Marked,
Mileages = v.Mileages.Select(m => new MileageDto
{
MileageDate = m.MileageDate,
RecordedMileage = m.RecordedMileage
})
.ToList(),
})
.ToList();
return (IEnumerable<VehicleDto>) query.AsEnumerable();
this doesn't seem to be the most elegant way of doing this, if anyone could offer any advice but it does return what is required.
The DTO's look like:
public class VehicleDto
{
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<MileageDto> Mileages { get; set; }
}
and
public class MileageDto
{
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
}
Thanks for taking the time to look at this
Tuppers

EF projections many to many not loading

I have four classes defined as follows:
public class Operator : Base
{
public string Name { get; set; }
public string URL { get; set; }
public ICollection<Address> Addresses { get; set; }
public ICollection<Contact> Contacts { get; set; }
public ICollection<Application.Application> Applications { get; set; }
}
public class Address : Base
{
public String Street{ get; set; }
public int? ParentId { get; set; }
public Operator Parent { get; set; }
public ICollection<Application.Application> Applications { get; set; }
}
public class Contact : Base
{
public string Name { get; set; }
public int? ParentId { get; set; }
public Operator Parent { get; set; }
public ICollection<Application.Application> Applications { get; set; }
}
public class Application : Base
{
[MaxLength(300)]
public String Name { get; set; }
public ICollection<Operator.Operator> Operators { get; set; }
public ICollection<Operator.Address> Addresses { get; set; }
public ICollection<Operator.Contact> Contacts { get; set; }
}
public class Base
{
public int Id { get; set; }
//public int BaseObjectId { get; set; }
TimeZoneInfo _easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
private DateTime _modifiedDate;
public DateTime ModifiedDate
{
get { return this._modifiedDate; }
set
{
this._modifiedDate = DateTime.SpecifyKind(value, DateTimeKind.Unspecified);
this._modifiedDate = TimeZoneInfo.ConvertTimeFromUtc(this._modifiedDate, _easternZone);
}
}
private DateTime _createdDate;
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate
{
get { return this._createdDate; }
set
{
this._createdDate = DateTime.SpecifyKind(value, DateTimeKind.Unspecified);
this._createdDate = TimeZoneInfo.ConvertTimeFromUtc(this._createdDate, _easternZone);
}
}
public bool Disabled { get; set; }
}
public class DB : DbContext
{
public DbSet<EF.Complaint.Complaint> Complaints { get; set; }
public DbSet<EF.Category.Category> Categories { get; set; }
public DbSet<EF.Action.Action> Actions { get; set; }
public DbSet<EF.Medium.Medium> Mediums { get; set; }
public DbSet<EF.Priority.Priority> Priorities { get; set; }
public DbSet<EF.Complaint.Comment> Comments { get; set; }
public DB()
{
this.Database.Log = s => { System.Diagnostics.Debug.WriteLine(s); };
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
modelBuilder.Configurations.Add(new ComplaintConfig());
modelBuilder.Configurations.Add(new CategoryConfig());
modelBuilder.Configurations.Add(new ActionConfig());
modelBuilder.Configurations.Add(new MediumConfig());
modelBuilder.Configurations.Add(new PriorityConfig());
modelBuilder.Configurations.Add(new CommentConfig());
base.OnModelCreating(modelBuilder);
}
}
Operator, Contact and Address can all belong to a particular application. So you could have a structure like this:
Operator 1 - belongs to App1 and App2
Child contact 1 - belongs to App1
Child Contact 2 - belongs to App2
Child Address 1 - belongs to App2
I am trying to build a method that returns a list of Operators for a particular Application and includes Addresses and Contacts of that operator that also belong to that Application
Here is a query I have concocted so far
public IEnumerable<Operator> GetForApp(string name)
{
return (Context.Operators.Where(x => x.Applications.Any(y => y.Name == name))
.Select(x => new
{
x,
Addresses = x.Addresses.Where(y => y.Applications.Any(z => z.Name == name)),
Contacts = x.Contacts.Where(y => y.Applications.Any(z => z.Name == name)),
Applications = x.Applications
}).AsEnumerable()
.Select(n => n.x));
}
This works in a sense that the basic members of Operator get loaded as well as Addresses and Contacts get loaded and filtered correctly...What doesn't get loaded is Applications and I can't figure out why. They only difference I see is that Addresses/Contacts and Operator is many-to-one and Applications and Operator is many-to-many.
You must use lazy loading feature or include your related object directly in your query.
public class Operator : Base
{
public string Name { get; set; }
public string URL { get; set; }
public ICollection<Address> Addresses { get; set; }
public ICollection<Contact> Contacts { get; set; }
// by adding virtual keyword EF could generate proxy classes and
// fetch actual objects when are needed.
public virtual ICollection<Application.Application> Applications { get; set; }
}
Or in your query directly include Application:
public IEnumerable<Operator> GetForApp(string name)
{
return (Context.Operators.Where(x => x.Applications.Any(y => y.Name == name))
.Include(x=>x.Applications)
.Select(x => new
{
x,
Addresses = x.Addresses.Where(y => y.Applications.Any(z => z.Name == name)),
Contacts = x.Contacts.Where(y => y.Applications.Any(z => z.Name == name)),
Applications = x.Applications
}).AsEnumerable()
.Select(n => n.x));
}

Why are my entities not being lazy loaded?

I'm using EF 6 and defining my database with Code First.
The following line of code returns a Transaction entity, however the EndorsementInfo navigation property is null. I've checked the database and there is definitely data for the test data. "var trans" does appear to have a valid IQueryable, but navigation property t.EndorsementInfo is null when it shouldn't be.
var trans = unitOfWork.GetRepository<Transaction>().GetAll().Where(t => t.PolicyId == command.PolicyId);
results.Transactions = new List<TransactionListItem>();
foreach (var t in trans)
{
results.Transactions.Add(new TransactionListItem
{
Id = t.Id,
EffDate = t.EffectiveDate,
EffectiveDate = t.EffectiveDate.ToShortDateString(),
TransactionType = t.TransactionType.ToStringValue(),
EndorsementType = t.TransactionType == TransactionType.Endorsement ?
t.EndorsementInfo.EndorsementType.Description : ""
});
}
Transaction Entity:
public class Transaction : EntityBase
{
[Required]
public TransactionType TransactionType { get; set; }
public long PolicyId { get; set; }
public virtual Policy Policy { get; set; }
[Required]
public DateTime EffectiveDate { get; set; }
public DateTime? ExpirationDate { get; set; }
public string Description { get; set; }
public virtual Quote QuoteInfo { get; set; }
public virtual Cancellation CancellationInfo { get; set; }
public virtual NewBusiness NewBusinessInfo { get; set; }
public virtual Endorsement EndorsementInfo { get; set; }
}
Endorsement Entity
public class Endorsement : EntityBase
{
public Transaction Transaction { get; set; }
public long EndorsementTypeId { get; set; }
public virtual EndorsementType EndorsementType { get; set; }
public int EndorsementNum { get; set; }
[MaxLength(500)]
public string EndorsementDesc { get; set; }
public Decimal? Premium { get; set; }
}
Code First Fluent Configurations
public class TransactionConfiguration : EntityTypeConfiguration<Transaction>
{
public TransactionConfiguration()
{
HasOptional(t => t.QuoteInfo).WithRequired(q => q.Transaction);
HasOptional(t => t.NewBusinessInfo).WithRequired(q => q.Transaction);
HasOptional(t => t.EndorsementInfo).WithRequired(q => q.Transaction);
HasOptional(t => t.CancellationInfo).WithRequired(q => q.Transaction);
}
}
Repositories implementation of GetAll
public IQueryable<T> GetAll(string include)
{
return _set.Include(include);
}
I've checked and rechecked that everything is set up correctly. I don't know what I could be missing.
Thanks.
You are using an opened connection to execute two data readers, you need to enable the multiple result set in the connection string.
MultipleActiveResultSets=True;

Entity Framework Code First and Invalid Object Name Error

I have a composite table called ImporterState, that are tied to a table called Importer and State. The error happens here context.Importers.Include(q => q.States). Why is this happening?
{"Invalid object name 'ImporterStates'."}
[Table("HeadlineWebsiteImport", Schema = "GrassrootsHoops")]
public class Importer
{
public int Id { get; set; }
public string Name { get; set; }
public string RssUrl { get; set; }
public string Type { get; set; }
public string Keywords { get; set; }
public bool Active { get; set; }
public DateTime DateModified { get; set; }
public DateTime DateCreated { get; set; }
public int WebsiteId { get; set; }
public HeadlineWebsite Website { get; set; }
[InverseProperty("Importers")]
public ICollection<State> States { get; set; }
}
[Table("State", Schema = "GrassrootsHoops")]
public class State
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Abbr { get; set; }
[InverseProperty("States")]
public ICollection<Headline> Headlines { get; set; }
[InverseProperty("States")]
public ICollection<Importer> Importers { get; set; }
}
The many to many is not possible using attributes only.
try using something like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Importer>()
.HasMany(i => i.States)
.WithMany(s => s.Importers)
.Map(m =>
{
m.MapLeftKey("ImporterId");
m.MapRightKey("StateId");
m.ToTable("ImporterState");
});
}