Mikael Eliasson
Mikael Eliasson

Reputation: 5227

EF Core adding discriminator column to owned entity

So my entity has two properties with an owned entity. For some reason EF Core is adding a discriminator column to one of them. It would be great if someone could help me figure out what I'm doing wrong or if there is some weird bug here.

This is the whole entity even though I suspect only Requested and Approved is relevant.

public class Absence
    {
        public Absence()
        {
            Requested = new AuditInfo();
            Approved = new AuditInfo();
        }

        public Guid Id { get; set; }

        public DateTime StartDate { get; set; }
        public DateTime? EndDate { get; set; }

        public AbsenceType Type { get; set; }
        public AbsenceState State { get; set; }

        public AuditInfo Requested { get; set; }
        public AuditInfo Approved { get; set; }

        public Guid EmployeeId { get; set; }
        public Employee Employee { get; set; }

        public ICollection<AbsenceDay> Days { get; set; }

    }

Absence doesn't have any special mappings but AuditInfo is mapped as

x.Owned<AuditInfo>();

This mostly generate the expected SQL, except this weird item:

enter image description here

This also leads to the code failing at runtime with this message:

Message: Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
---- System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'ApprovedA_Discriminator', table 'bokio-test.dbo.Absences'; column does not allow nulls. INSERT fails.
The statement has been terminated.

What I have tried:

I tried mapping the items like this which makes no difference:

        x.Entity<Absence>().OwnsOne(x1 => x1.Approved);
        x.Entity<Absence>().OwnsOne(x1 => x1.Requested);

I tried renaming Approved to ApprovedA which made no difference.

I then tried a small repro like this which doesn't create a discriminator column:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Address HomeAddress { get; set; }
    public Address WorkAddress { get; set; }

}

Now I'm out of ideas and need some help.


Update 1:

Hold your hats, things are about to get really weird!

When I rename Approved to Donkey the discriminator column goes away 🤯

enter image description here

Almost stranger is that when I try to repro it with the name Approved in the small test I still don't get the discriminator column there.


Update 2:

So I tried a bunch of other names. Success means no discriminator column.

Xpproved = success App = fail Ap = fail CrAp = success A = fail Axx = success ApprovedXX = fail XApproved = success

That inspired me to try this solution with XApproved which works

x.Entity<Absence>().OwnsOne(x1 => x1.XApproved, o =>
{
    o.Property(x2 => x2.Ip).HasColumnName("Approved_Ip");
    o.Property(x2 => x2.UserAgent).HasColumnName("Approved_UserAgent");
    o.Property(x2 => x2.UserId).HasColumnName("Approved_UserId");
    o.Property(x2 => x2.TimeUtc).HasColumnName("Approved_TimeUtc");
});

Update 3:

AuditInfo only has these properties and no mappings beside being Owned. It does have a bunch of classes inheriting it though:

public DateTime TimeUtc { get; set; }
public Guid? UserId { get; set; }
public string Ip { get; set; }
public string UserAgent { get; set; }

Upvotes: 3

Views: 2920

Answers (1)

Mikael Eliasson
Mikael Eliasson

Reputation: 5227

While I don't fully understand the issue (why changing the name made the problem go away) I noticed that it didn't really solve the problem. But rather just moved it around. The comment comment from @philreed above was what really made it click.

AuditInfo was inherited by another Owned Entity ClosurePart. By changing from

public class AuditInfo {
...props here
}

public class ClosurePart : AuditInfo {}

to

public class AuditInfoCommon {
...props here
}

public class AuditInfo : AuditInfoCommon {}

public class ClosurePart : AuditInfoCommon {}

The problem went away fully without needing the hacks.

NOTE: AuditInfoCommon is not mapped into the model!

Upvotes: 2

Related Questions