EF one-to-many to assure the link with the main table.

sblb 1,166 Reputation points
2022-11-12T15:27:52.21+00:00

Hi,
I would like to do a link between main table Developer & a table ActionList.

The ActionList take place in edit mode of my app to the address : "/developer/edit/{developerId:int}"

I try to use EF one-to-many to do the link between two tables

I structured the model class has below :

public partial class Developer  
     {  
         public int Id { get; set; }  
          public ICollection<ActionsList>? ActionsLists { get; set; }  
  }  
      
  public class ActionsList  
     {  
         [Key]     
         public int ActionId { get; set; }              
           
         public DateTime ProductionDate { get; set; }  
              
         public int DeveloperId { get; set; }  
         public Developer Developer { get; set; }  
      
     }  

application data :

 public class ApplicationDBContext : DbContext  
     {  
         public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options)  
         {  
         }  
         public DbSet<Developer> Developers { get; set; }  
         public DbSet<ActionsList> ActionsLists { get; set; }  
                     
          
         protected override void OnModelCreating(ModelBuilder modelBuilder)  
         {  
             modelBuilder.Entity<ActionsList>()  
                         .HasOne<Developer>(s => s.Developer)  
                         .WithMany(g => g.ActionsLists)  
                         .HasForeignKey(s => s.DeveloperId);         
         }     
          
       }  

when I want to add the actions in page https://localhost:44354/developer/edit/1 this means all actions for developerId = 1 ; any value has posted in ActionList.

But when I've put the break point await http.PostAsJsonAsync("api/actionslist", act); I see that ActionId = null .

I guess that I set up EF one-to-many wrong!

Can you help me how to link the two tables? (This is the first time I've done this)

Thanks in advance

Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,349 questions
{count} votes

Accepted answer
  1. AgaveJoe 25,866 Reputation points
    2022-11-18T12:45:54.757+00:00

    Against my better judgement, I created an example. This is an example NOT a solution. In production code you would NOT pass entities between the client and Web API! Typically you would designed a data interrace and pass poco classes.

    I used followed the following documentation which I have provided many times over many of your threads.

    Tutorial: Create a web API with ASP.NET Core
    Saving Related Data

    Entities

        public partial class Developer  
        {  
            public int DeveloperId { get; set; }  
            public List<ActionItem>? ActionItems { get; set; }  
        }  
      
        public class ActionItem  
        {  
            public int ActionItemId { get; set; }  
            public string? Tilte { get; set; }  
            public string? Description { get; set; }  
            public string? State { get; set; }  
            public DateTime? OpenDate { get; set; }  
            public DateTime? PlanDate { get; set; }  
            public DateTime? CloseDate { get; set; }  
      
            public int DeveloperId { get; set; }  
            public Developer? Developer { get; set; }  
          
    

    Related data configuration

            protected override void OnModelCreating(ModelBuilder modelBuilder)  
            {  
                modelBuilder.Entity<ActionItem>()  
                    .HasOne(d => d.Developer)  
                    .WithMany(d => d.ActionItems);  
            }  
    

    Controller

        [Route("api/[controller]")]  
        [ApiController]  
        public class DeveloperActionController : ControllerBase  
        {  
            private readonly WebApiSqliteContext _context;  
            public DeveloperActionController(WebApiSqliteContext context)  
            {  
                _context = context;  
            }  
            // GET: api/<DeveloperActionController>  
            [HttpGet]  
            public IEnumerable<Developer> GetDevelopersWithActions()  
            {  
                return _context.Developers.Include(a => a.ActionItems).ToList();  
            }  
      
            // GET api/<DeveloperActionController>/5  
            [HttpGet("{id}")]  
            public async Task<ActionResult<Developer>> GetDeveloperActions(int id)  
            {  
                Developer? developer = await _context.Developers  
                    .Include(a => a.ActionItems)  
                    .FirstOrDefaultAsync(d => d.DeveloperId == id);  
      
                if (developer == null)  
                {  
                    return NotFound();  
                }  
      
                return Ok(developer);  
            }  
      
            // POST api/<DeveloperActionController>  
            [HttpPost]  
            public async Task<ActionResult<Developer>> PostAsync([FromBody] Developer developer)  
            {  
                _context.Developers.Add(developer);  
                await _context.SaveChangesAsync();  
      
                return CreatedAtAction(nameof(GetDeveloperActions), new { id = developer.DeveloperId }, developer);  
            }  
      
            // PUT api/<DeveloperActionController>/5  
            [HttpPut("{id}")]  
            public async Task<ActionResult<Developer>> Put(int id, [FromBody] ActionItem actionItem)  
            {  
                Developer? developer = await _context.Developers  
                    .Include(a => a.ActionItems)  
                    .FirstOrDefaultAsync(d => d.DeveloperId == id);  
      
                if (developer == null)  
                {  
                    return NotFound();  
                }  
      
                developer.ActionItems.Add(actionItem);  
                await _context.SaveChangesAsync();  
      
                return CreatedAtAction(nameof(GetDeveloperActions), new { id = developer.DeveloperId }, developer);  
      
            }  
      
            // DELETE api/<DeveloperActionController>/5  
            [HttpDelete("{id}")]  
            public async Task<IActionResult> Delete(int id)  
            {  
                Developer? developer = await _context.Developers  
                    .Include(a => a.ActionItems)  
                    .FirstOrDefaultAsync(d => d.DeveloperId == id);  
      
                if (developer == null)  
                {  
                    return NotFound();  
                }  
      
                _context.Remove(developer);  
      
                await _context.SaveChangesAsync();  
      
                return NoContent();  
            }  
        }  
    

    Post data

    {  
      "developerId": 0,  
      "actionItems": [  
        {  
          "actionItemId": 0,  
          "tilte": "Action Title",  
          "description": "Description",  
          "state": "State",  
          "openDate": "2022-11-17",  
          "planDate": "2022-11-17",  
          "closeDate": "2022-11-17",  
          "developerId": 0  
        }  
      ]  
    }  
      
    

    Put data

    {  
      "actionItemId": 0,  
      "tilte": "Action Title 2",  
      "description": "Description 2",  
      "state": "State 2",  
      "openDate": "2022-11-18",  
      "planDate": "2022-11-18",  
      "closeDate": "2022-11-18",  
      "developerId": 1  
    }  
    

    Configuration is needed to return a entity otherwise you'll get an error.

    builder.Services.AddControllers().AddJsonOptions(x =>  
                    x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);  
    
    0 comments No comments

13 additional answers

Sort by: Most helpful
  1. sblb 1,166 Reputation points
    2022-11-14T10:16:04.38+00:00

    @Zhi Lv - MSFT thanks to your comments

    Do you mean after calling the API method, the ActionId in the return data is null?

    It's ok ActionId is not null and auto-insert.

    As you can see in the picture below that I've Id et DeveloperId column.

    the Id column is in the class model definition and DeveloperId was created automatically
    This means that DeveloperId is the foreign key according to the class model below

    public partial class Developer
    {
    public int Id { get; set; }
    public ICollection<ActionsList>? ActionsLists { get; set; }
    }

    public class ActionsList  
       {  
           [Key]     
           public int ActionId { get; set; }              
    
           public DateTime ProductionDate { get; set; }  
    
           public int Id { get; set; }  
           public Developer Developer { get; set; }  
    
       }  
    

    260000-image.png

    the action method is

    [HttpGet]
    public async Task<IActionResult> Get()
    {
    var actions = await _contexts.ActionsLists.ToListAsync();
    return Ok(actions);
    }

        [HttpGet("{id}")]  
        public async Task<IActionResult> Get(int id)  
        {  
            var action = await _contexts.ActionsLists.FirstOrDefaultAsync(a => a.Id == id);  
            return Ok(action);  
        }  
    
        [HttpPost]  
        public async Task<IActionResult> Post(ActionsList actions)  
        {  
            _contexts.Add(actions);  
            await _contexts.SaveChangesAsync();  
            return Ok(actions.ActionId);  
        }  
    

    How I can see only the actions for the page /developer/edit/1 ? because right now I see all actions for each edit page


  2. sblb 1,166 Reputation points
    2022-11-16T09:22:43.54+00:00

    I follow up your recommendation and I rename the controller and the class model.
    the controller name is : DeveloperActionsController
    the class model name is : ActionItem.

    I come back to the initial problem.
    I follow up the link configure-one-to-many-relationship-using-fluent-api-in-ef-core.aspx to create the foreign key to link the main table Developer with ActionItem table.

    I've put the break point to [HttpPost] and I obtained : PRimaryKey ActionId= 0 and the ForeignKey CurrentActionId = 0
    Do you see my mistake?

      [HttpPost]  
            public async Task<IActionResult> Post(ActionItem actions)  
            {  
                _contexts.Add(actions);  
                await _contexts.SaveChangesAsync();  
                return Ok(actions.ActionId);  
            }  
      
    

    260846-image.png

    260867-image.png


  3. sblb 1,166 Reputation points
    2022-11-16T15:18:07.55+00:00

    I tried to follow up but I don't understand clear.
    plus in your link there is a github link with the sample. Here RelatedData

    I try to do the same class model but w/o succes.
    Can you help me on it?


  4. sblb 1,166 Reputation points
    2022-11-13T20:06:51.497+00:00

    I changed the application data as below

      public class ApplicationDBContext : DbContext  
          {  
              public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options)  
              {  
              }  
              public DbSet<Developer> Developers { get; set; }  
              public DbSet<ActionsList> ActionsLists { get; set; }                       
                   
            }  
    

    I changed the model class as below

     public partial class Developer  
          {  
              public int Id { get; set; }  
              public ICollection<ActionsList>? ActionsLists { get; set; }  
       }  
              
       public class ActionsList  
          {  
              [Key]     
              public int ActionId { get; set; }              
                   
              public DateTime ProductionDate { get; set; }  
                      
              public int Id { get; set; }  
              public Developer Developer { get; set; }  
              
          }  
    

    After that I've made a migration and the foreign key was created DeveloperId. The result of the migration is

       constraints: table =>  
                    {  
                        table.PrimaryKey("PK_ActionsLists", x => x.Id);  
                        table.ForeignKey(  
                            name: "FK_ActionsLists_Developers_DeveloperId",  
                            column: x => x.DeveloperId,  
                            principalTable: "Developers",  
                            principalColumn: "Id");  
                    });  
      
                migrationBuilder.CreateIndex(  
                    name: "IX_ActionsLists_DeveloperId",  
                    table: "ActionsLists",  
                    column: "DeveloperId");  
    

    But I can't make the link between /developer/edit/{developerId:int}" and the actions await http.PostAsJsonAsync("api/actionslist", act)

    This can be translated as follows:
    /developer/edit/1 ==> eg 4 actions have been created for line 1 edited
    /developer/edit/2 ==> eg 10 actions have been created for line 2 edited

    also in edit page I would like to see only actions concerning /developer/edit/1 or /developer/edit/2.

    I will appreciate your help!

    0 comments No comments