I'm trying to write a small web api and it is my first time using entity framework.
My problem is that only some of my data gets saved.
I have this GET method which is just supposed to save a model into the database:
[HttpGet]
public async Task<IActionResult> PopulateDB()
{
var playerOneQuest = new StatusModel
{
PlayerId = "1",
points= 250,
completedSteps = new List<Status>()
{
new Status
{
Id = 11,
Index = 1
}
}
};
_context.Quest.Add(playerOneQuest);
await _context.SaveChangesAsync();
return Ok("Added data"); // Breakpoint here
}
When I add a breakpoint at the last line _context looks perfectly fine with all the data, but as soon as the method has executed completedSteps will be null. The other data - PlayerId & points - are saved just fine.
I have tried adding this _context.Entry(playerOneQuest).State = EntityState.Added; before calling SaveChangesAsync(), but completedSteps still ends up null after the method has executed.
_context is defined as:
public class QuestContext : DbContext
{
public QuestContext(DbContextOptions<QuestContext> options)
: base(options){ }
public virtual DbSet<StatusModel> Quest { get; set; } = null!;
}
The 2 models used are defined as:
public class StatusModel
{
[Key]
public string? PlayerId {get;set;}
public long Points{get;set;}
public List<Status> completedSteps {get;set;} = null!;
}
public class Status
{
public long Id {get;set;}
public int Index {get;set;}
}
And for good measure, this is how the database context is added to my web app:
builder.Services.AddDbContext<QuestContext>(opt =>
opt.UseInMemoryDatabase("Quests"));
CodePudding user response:
The problem is that you don't specify that you want to load additional data.
https://docs.microsoft.com/en-us/ef/core/querying/related-data/
You can either enable lazy loading via proxies: https://docs.microsoft.com/en-us/ef/core/querying/related-data/lazy or be explicit by using the .Include() on the query.
var completeList = await _questContext.Quest.Include(i => i.completedSteps).ToListAsync();
Your entities are correctly setup by following the conventions as far as I can tell. This means that you don't need to configure anything additional regarding the entities - when you just want to have a simple one-to-many relationship.
Example on a controller where the QuestContext is injected:
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
private readonly QuestContext _questContext;
public MyController(QuestContext questContext)
{
_questContext = questContext;
}
[HttpGet]
public async Task<NoContentResult> Get()
{
var playerOneQuest = new StatusModel
{
PlayerId = "1",
Points = 250,
completedSteps = new List<Status>()
{
new Status
{
Id = 11,
Index = 1
}
}
};
_questContext.Quest.Add(playerOneQuest);
await _questContext.SaveChangesAsync();
return NoContent();
}
[HttpGet("check")]
public async Task<List<StatusModel>> Check()
{
var completeList = await _questContext.Quest.Include(i => i.completedSteps).ToListAsync();
return completeList;
}
}
CodePudding user response:
In models:
public class StatusModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id {get;set;}
public long Points{get;set;}
public List<Status> completedSteps {get;set;} = new List<Status>();
}
public class Status
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id {get;set;}
public int Index {get;set;}
public long StatusModelId {get;set;}
public StatusModel {get;set;}
}
In your storage context in OnModelCreating:
modelBuilder.Entity<Status>().HasOne(c => c.StatusModel).WithMany(c => c.completedSteps)
.HasForeignKey(c => c.StatusModelId).OnDelete(DeleteBehavior.Cascade);
You have to set up the relations between objects. You can do it in your Context with fluent API, or with attributes directly in the models. Also in your context you need both DbSets, for status and for statusModel.
Wrote the answer without testing, should be close enough though. Here is a link to relations: Relations in EF
I missed your PlayerId - this is I guess an FK to Player? Than you have to set up that relation as well. If you intended it as PK, it probably should not be nullable.
CodePudding user response:
I think you should use [HttpPost], not [HttpGet]
