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