Ceci Semble Absurde.
Ceci Semble Absurde.

Reputation: 530

Why my EF6 generated relationship not working?

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 ?

Edit 1

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

Answers (2)

Ceci Semble Absurde.
Ceci Semble Absurde.

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

Michael Mairegger
Michael Mairegger

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

Related Questions