I'm having following configurations and relationships. i.e. when a Meal is deleted, relevant MealSubMeals should get deleted but not SubMeal. This is working as expected with following configuration. However, I don't want any MealSubMeals get deleted when a SubMeal is deleted. Instead, EF should throw an error when attempted to delete a SubMeal while MealSubMeals exist.
How do I achieve above? I probably don't want any cyclic relationships within my db.
Thanks!
public class Meal
{
public int Id { get; set; }
public string? MealName { get; set; }
public ICollection<MealSubMeal>? MealSubMeals { get; set; }
}
public class MealSubMeal
{
public int Id { get; set; }
public int MealId { get; set; }
public Meal? Meal { get; set; }
public bool Optional { get; set; }
public decimal SubMealPrice { get; set; }
public int SubMealId { get; set; }
public SubMeal? SubMeal { get; set; }
}
public class SubMeal
{
public int Id { get; set; }
public string? SubMealName { get; set; }
public string? SubMealDescription { get; set; }
}
public class MealConfiguration : IEntityTypeConfiguration<Meal>
{
public void Configure(EntityTypeBuilder<Meal> builder)
{
builder.Property(t => t.MealName).HasMaxLength(40).IsRequired();
builder.HasIndex(t => t.MealName).IsUnique();
builder.Property(t => t.Price).HasPrecision(10, 2).IsRequired();
}
}
public class MealSubMealConfiguration : IEntityTypeConfiguration<MealSubMeal>
{
public void Configure(EntityTypeBuilder<MealSubMeal> builder)
{
builder.HasOne(t => t.Meal).WithMany(t => t.MealSubMeals).HasForeignKey(t => t.MealId).IsRequired();
builder.HasOne(t => t.SubMeal).WithMany().HasForeignKey(t => t.SubMealId).IsRequired();
builder.HasIndex(t => new { t.MealId, t.SubMealId }).IsUnique();
builder.Property(t => t.SubMealPrice).HasPrecision(10, 2).IsRequired();
}
}
public class SubMealConfiguration : IEntityTypeConfiguration<SubMeal>
{
public void Configure(EntityTypeBuilder<SubMeal> builder)
{
builder.Property(t => t.SubMealName).HasMaxLength(40).IsRequired();
builder.HasIndex(t => t.SubMealName).IsUnique();
builder.Property(t => t.SubMealDescription).HasMaxLength(200);
}
}
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.
I am unable to configure EntityFramework migration to create DB structure by the hierarchy provided.
Consider the following:
public class EntityBase
{
public EntityBase()
{
UId = Guid.NewGuid();
CreateTime = DateTime.UtcNow;
}
[Key]
public Guid UId { get; set; }
}
public class Matrix : EntityBase
{
public Guid PassportUId { get; set; }
public virtual Contracts.Entities.Passport Passport { get; set; }
public DeviceBlockType Type { get; set; }
public virtual ICollection<DeviceBlockBase> Blocks { get; set; }
}
public class DeviceBlockBase: EntityBase
{
public string State { get; set; }
public Guid? MatrixUId { get; set; }
public virtual Matrix Matrix { get; set; }
}
public class ArrayBlock: DeviceBlockBase
{
public string LotNumber { get; set; }
public string TransNum { get; set; }
public Guid? LockoutByUId { get; set; }
public virtual User LockoutBy { get; set; }
public DateTime? LockoutFrom { get; set; }
public DateTime? LockoutTill { get; set; }
public Guid? LockoutReasonUId { get; set; }
public virtual LockoutReason LockoutReason { get; set; }
}
public class Container : ArrayBlock
{
public DateTime FillDate { get; set; }
public bool? IsLearned { get; set; }
}
public class Cartridge: ArrayBlock
{
public DateTime LoadDate { get; set; }
public Guid LoaderUid { get; set; }
public User Loader { get; set; }
public bool IsOpen { get; set; }
}
public class Vault: DeviceBlockBase
{
public DateTime FillDate { get; set; }
}
Edited (Configuration class):
public abstract class BaseBlockDeviceConfiguration<T> : EntityTypeConfiguration<T> where T : DeviceBlockBase
{
protected BaseBlockDeviceConfiguration()
{
HasOptional(x => x.MedicationMatrix)
.WithMany()
.HasForeignKey(x => x.MedicationMatrixUId);
}
}
public abstract class BaseArrayBlockConfiguration<T> : BaseBlockDeviceConfiguration<T> where T : ArrayBlock
{
protected BaseArrayBlockConfiguration()
{
HasOptional(x => x.LockoutBy)
.WithMany()
.HasForeignKey(x => x.LockoutByUId)
.WillCascadeOnDelete(false);
HasOptional(x => x.LockoutReason)
.WithMany()
.HasForeignKey(x => x.LockoutReasonUId)
.WillCascadeOnDelete(false);
}
}
public class CartridgeConfiguration : BaseArrayBlockConfiguration<Cartridge>
{
public CartridgeConfiguration()
{
HasOptional(p => p.Loader)
.WithMany()
.HasForeignKey(p => p.LoaderUid)
.WillCascadeOnDelete(false);
}
}
public class ContainerConfiguration : BaseArrayBlockConfiguration<Container>
{
public ContainerConfiguration()
{
}
}
public class VaultConfiguration : BaseBlockDeviceConfiguration<Vault>
{
public VaultConfiguration()
{
}
}
The EntityTypeConfiguration builds the DB in somewhat weird structure:
RenameTable(name: "dbo.Cartridges", newName: "DeviceBlockBases");
AddColumn("dbo.DeviceBlockBases", "FillDate", c => c.DateTime());
AddColumn("dbo.DeviceBlockBases", "IsLearned", c => c.Boolean());
AddColumn("dbo.DeviceBlockBases", "FillDate1", c => c.DateTime());
AddColumn("dbo.DeviceBlockBases", "FillDate2", c => c.DateTime());
AddColumn("dbo.DeviceBlockBases", "Discriminator", c => c.String(nullable: false, maxLength: 128));
DropTable("dbo.Containers");
It looks like EF CodeFirst refuses to generate polymorphism and is going wild.
Any ideas?
Thanks ahead
I have 2 entities:
public partial class GPSdevice
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public GPSdevice()
{
}
public int ID { get; set; }
public string UserName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual Truck Truck { get; set; }
}
public partial class Truck
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Truck()
{
}
public int TruckID { get; set; }
public string TruckNo { get; set; }
public string Make { get; set; }
[ForeignKey("GPSdevice")]
public int? GPSdeviceID { get; set; }
public virtual GPSdevice GPSdevice { get; set; }
}
I want to create a relationship one-to-one-or-zero (each GPSdevice can be linked to any truck (but to one and only one) or not linked at all)
I write the following code:
modelBuilder.Entity<GPSdevice>()
.HasOptional(e => e.Truck)
.WithOptionalPrincipal()
.WillCascadeOnDelete(false);
but it creates the following migration:
public override void Up()
{
AddColumn("dbo.Truck", "GPSdevice_ID", c => c.Int());
CreateIndex("dbo.Truck", "GPSdeviceID");
CreateIndex("dbo.Truck", "GPSdevice_ID");
AddForeignKey("dbo.Truck", "GPSdeviceID", "dbo.GPSdevices", "ID");
AddForeignKey("dbo.Truck", "GPSdevice_ID", "dbo.GPSdevices", "ID");
}
why it creates one more field and how to use the current field GPSdeviceID instead?
ADDED:
If I remove
public int? GPSdeviceID { get; set; }
and add MapKey:
modelBuilder.Entity<GPSdevice>()
.HasOptional(e => e.Truck)
.WithOptionalPrincipal(e=>e.GPSdevice).Map(p=>p.MapKey("GPSdeviceID"))
.WillCascadeOnDelete(false);
in result I get the following migration code:
public override void Up()
{
CreateIndex("dbo.Truck", "GPSdeviceID");
AddForeignKey("dbo.Truck", "GPSdeviceID", "dbo.GPSdevices", "ID");
}
then I get the following error:
There are no primary or candidate keys in the referenced table
'dbo.GPSdevices' that match the referencing column list in the foreign
key 'FK_dbo.Truck_dbo.GPSdevices_GPSdeviceID'. Could not create
constraint or index. See previous errors.
Use this :
public partial class GPSdevice
{
public GPSdevice()
{
}
public int ID { get; set; }
public string UserName { get; set; }
public virtual Truck Truck { get; set; }
}
public partial class Truck
{
public Truck()
{
}
public int TruckID { get; set; }
public string TruckNo { get; set; }
public string Make { get; set; }
public virtual GPSdevice GPSdevice { get; set; }
}
modelBuilder.Entity<GPSdevice>()
.HasOptional(o => o.Truck)
.WithRequired(ad => ad.GPSdevice);
modelBuilder.Entity<Truck>()
.HasKey(e => e.TruckID);
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
I have Many To Many relationship defined and when I try to query for the records that should be in the map I get null.
public class Record
{
public int RecordId { get; set; }
public DateTime DateRecordCreated { get; set; }
public ICollection<Street> Streets { get; set; }
public ICollection<Street> CrossStreets { get; set; }
}
public class RecordMap : EntityTypeConfiguration<Record>
{
public RecordMap()
{
// Primary Key
this.HasKey(t => t.RecordId);
this.HasMany(r => r.Streets)
.WithMany(c => c.Records)
.Map(sl =>
{
sl.ToTable("StreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.HasMany(r => r.CrossStreets)
.WithMany(c => c.AnotherRecord)
.Map(sl =>
{
sl.ToTable("AnotherStreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.Property(t => t.DateRecordCreated).IsRequired();
}
}
public class House : Record
{
public string HouseNumber { get; set; }
public string StreeName { get; set; }
public int ZipCode { get; set; }
}
public class Street
{
public int StreetId { get; set; }
public string StreetName { get; set; }
public ICollection<Record> Records { get; set; }
public ICollection<Record> AnotherRecord { get; set; }
}
Now when I run the following query below I get houses.CrossStreets as null, I tried adding enabling lazy loading and had the same out come.
public static void GetRecords()
{
using (var context = new SandboxContext())
{
var entities = context.Houses.Include(r => r.CrossStreets);
var houses = entities.ToList();
}
}