I am a bit new to Entity Framework Core, and I tried lots of things but I still could not solve the problem that I faced.
I have entities PicnicBag
, Apple
and Napkin
. I created the tables with add-migration
and update-database
commands. Also a join table is created called PicnicBagApple
.
Rules: a PicnicBag
can contain more than one Apple
and a PicnicBag
can contain only one Napkin
. Apple
and Napkin
values are fixed. We do not need to record them. We only need to record PicnicBag
with POST API Swagger. But we need to store relations.
Entity classes are composed like that:
public class Napkin
{
public int Id { get; set; }
public string? NapkinColor { get; set; }
}
public class Apple
{
public int Id { get; set; }
public string AppleColor { get; set; }
public virtual ICollection<PicnicBag> PicnicBags { get; set; }
}
public class PicnicBag
{
public int Id { get; set; }
public string PicnicBagColor { get; set; }
public int PicnicBagSize { get; set; }
public int NapkinId { get; set; } // foreign key from Napkin to store related Id at PicnicBag table
public Napkin Napkin { get; set; } // one to one
public virtual ICollection<Apple> Apples { get; set; } // one to many
}
I also fill the entities for Napkin and Apple at initializer class like that in here:
protected override void onModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
new DbInitalizer(modelBuilder).Seed();
}
I tried and called the post method with this code:
public PicnicBag CreatePicnicBag(PicnicBag picnicbag)
{
// this part only inserts to PicnicBag but NOT to join table of PicnicBagApple table
// did not insert to Apple and Napkin that I wanted to have (Ok)
dbContext.PicnicBag.Attach(picnicbag);
dbContext.Entry(picnicbag).State = EntityState.Added;
dbContext.SaveChangesAsync();
return picnicbag;
}
I also tried detach but got cast exception. Is there a way to save only to join table PicnicBagApple
and to PicnicBag
table?
There is a workaround and according to your words the model needs some modification I think.
Model
public class Napkin
{
public int Id { get; set; }
public string? NapkinColor { get; set; }
public PicnicBag PicnicBag { get; set; } // one-to-one configuration
}
DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PicnicBag>()
.HasMany(e => e.Apples)
.WithMany(e => e.PicnicBags);
modelBuilder.Entity<Napkin>()
.HasOne(e => e.PicnicBag)
.WithOne(e => e.Napkin)
.HasForeignKey<PicnicBag>(e => e.NapkinId)
.IsRequired();
}
Method
public async Task<OkResult> ManyToMany()
{
ICollection<Apple> apples = new List<Apple> {
_context.Apple.FirstOrDefault(i => i.Id == 1) };
var napk = _context.Napkin.FirstOrDefault(i => i.Id == 2);
_context.PicnicBag.Add(new PicnicBag { PicnicBagColor = "Yellow", PicnicBagSize = 18, Napkin = napk , Apples = apples});
_context.SaveChanges();
return Ok();
}
This will only create the relationship in your PicnicBagApple
and the record in your `PicnicBag'.
However, as you configured in your model, if you want to add an relationship only between apple and picnicbag, with certain record whose napkin-and-picnicbag already exists in table picnicbag, it will get a duplicate error, so it can still be optimized.