Reputation: 530
I have two tables (Jobs
and Versions
) on a One to Many relationship in a PostgreSQL database. I have generated a DB Context and Models with dotnet-ef (EF6 Database-first) and created routes with JsonApiDotNetCore.
My two models :
// A Version (with one job)
[DisplayName("version")]
[Table("Versions")]
public partial class Version : Identifiable<long>
{
[Attr(PublicName = "id-version")]
public override long Id { get; set; }
[Attr(PublicName = "id-job")]
public long JobId { get; set; }
[Attr(PublicName = "name")]
public string Name { get; set; }
[Attr(PublicName = "job")]
public virtual Job JobIdNavigation { get; set; }
}
// A Job (with multiple Versions)
[DisplayName("job")]
[Table("Jobs")]
public partial class Job : Identifiable<long>
{
public Job()
{
this.Versions = new HashSet<Version>();
}
[Attr(PublicName = "id-job")]
public override long Id { get; set; }
[Attr(PublicName = "name")]
public string Name { get; set; }
[Attr(PublicName = "versions")]
public virtual ICollection<Version> Versions { get; set; }
}
And a DB Context :
public partial class TalendExplorerDbContext : DbContext
{
[...]
public virtual DbSet<Job> Jobs { get; set; }
public virtual DbSet<Version> Versions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasPostgresExtension("adminpack")
.HasPostgresExtension("ltree")
.HasAnnotation("Relational:Collation", "French_France.1252");
modelBuilder.Entity<Job>(entity =>
{
entity.HasKey(e => e.Id)
.HasName("Jobs_pkey");
entity.Property(e => e.Id)
.HasColumnName("job_id")
.UseIdentityAlwaysColumn();
[...]
});
modelBuilder.Entity<Version>(entity =>
{
entity.HasKey(e => e.Id)
.HasName("Versions_pkey");
entity.Property(e => e.Id)
.HasColumnName("version_id")
.UseIdentityAlwaysColumn();
entity.Property(e => e.JobId).HasColumnName("job_id");
[...]
entity.HasOne<Job>(d => d.JobIdNavigation)
.WithMany(p => p.Versions)
.HasForeignKey(d => d.JobId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("Versions_id_job_fkey");
});
OnModelCreatingPartial(modelBuilder);
}
[...]
}
With those, I can get a resource like a Job but there is no Versions linked:
{
"links": {
"self": "https://localhost:5001/jobs/2"
},
"data": {
"type": "jobs",
"id": "2",
"attributes": {
"name": "job2",
"versions": []
},
"links": {
"self": "https://localhost:5001/jobs/2"
}
}
}
Is this normal behavior ? How can I make my relationship work ?
As suggest by Michael Mairegger, I try to include the relationship on the request:
from https://localhost:5001/jobs/2
to https://localhost:5001/jobs/2?include=versions
but got an error:
{
"errors": [
{
"id": "4c6d79c9-0af7-419b-b89c-d3a61588b73a",
"status": "400",
"title": "The specified include is invalid.",
"detail": "Relationship 'versions' does not exist on resource 'jobs'.",
"source": { "parameter": "include" }
}
]
}
Upvotes: 1
Views: 157
Reputation: 530
As suggest by a co-worker (and Michael Mairegger), I had to specify relationship with JsonApiDotNetCore.
So I need to change annotations on models:
// A Version (with one job)
[DisplayName("version")]
[Table("Versions")]
public partial class Version : Identifiable<long>
{
[...]
[HasOne(PublicName = "job")]
public virtual Job JobIdNavigation { get; set; }
}
// A Job (with multiple Versions)
[DisplayName("job")]
[Table("Jobs")]
public partial class Job : Identifiable<long>
{
[...]
[HasMany(PublicName = "versions")]
public virtual ICollection<Version> Versions { get; set; }
}
Now, the request https://localhost:5001/jobs/2
show a version (relationship is working! hooray!):
{
"links": {
"self": "https://localhost:5001/jobs/2"
},
"data": {
"type": "jobs",
"id": "2",
"attributes": {
"inserted-date": "2021-07-07T00:00:00+02:00",
"modification-date": null,
"path": "test",
"purpose": "purpose",
"description": "job de test",
"name": "job1",
"description-capture": null,
"purpose-capture": null
},
"relationships": {
"versions": {
"links": {
"self": "https://localhost:5001/jobs/2/relationships/versions",
"related": "https://localhost:5001/jobs/2/versions"
}
}
},
"links": {
"self": "https://localhost:5001/jobs/2"
}
}
}
Refer to Michael Mairegger anwser to get the content of related items via include
keyword.
Upvotes: 0
Reputation: 7301
I am not familiar with JsonApiDotNetCore but I think the reason is the same as in ODATA. The API does not load related data because you did not request it. Otherwise it can happen that you accidently load the whole database because every data is somehow connected to any other data.
The API supports an include
query parameter where you can request the additional navigation properties. I think if you execute https://localhost:5001/jobs/2?include=versions
the versions shall be in the results set.
See: Including Relationships for further information.
Upvotes: 2