Search code examples
c#entity-framework-coreswaggerasp.net-core-webapimany-to-many

Is it possible to save only relation and a definite table in a many-to-many relation with Entity Framework Core?


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?


Solution

  • 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'.

    enter image description here enter image description here

    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.