简体   繁体   中英

One to one relation between two abstract class in EF Core

I have a problem to create one to one relation for two abstract class. Each class has one table and discriminator.

Classes are:

public abstract class ValuationBase
{
    public Guid Id { get; internal set; }
    public DateTime CreationDateTime { get; internal set; }

    public Owner Owner { get; internal set; }
}

public abstract class Valuation<TResult> : ValuationBase where TResult : ValuationResult
{
    public abstract ValuationType Type { get; }

    public abstract TResult Result { get; protected set; }
}

public class ScoringCardValuation : Valuation<ScoringCardValuationResult>
{
    .
    .
    .

    public override ScoringCardValuationResult Result { get; protected set; }

    public override ValuationType Type => ValuationType.ScoringCardValuation;
}

public class FirstChicagoValuation : Valuation<FirstChicagoValuationResult>
{
    .
    .
    .

    public override ValuationType Type => ValuationType.FirstChicagoValuation;

    public override FirstChicagoValuationResult Result { get; protected set; }
}


public abstract class ValuationResult
{
    public Guid Id { get; set; }
    public abstract ValuationType ValuationType { get; }
}

public class ScoringCardValuationResult : ValuationResult
{
    .
    .
    .

    public override ValuationType ValuationType => ValuationType.ScoringCardValuation;
}

public class FirstChicagoValuationResult : ValuationResult
{
    .
    .
    .

    public override ValuationType ValuationType => ValuationType.FirstChicagoValuation;
}

And my mapping is:

public class ValuationBaseMapping : IEntityTypeConfiguration<ValuationBase>
{
    public void Configure(EntityTypeBuilder<ValuationBase> builder)
    {
        builder.HasKey(c => c.Id);

        builder.HasDiscriminator()
            .HasValue<ScoringCardValuation>(nameof(ScoringCardValuation))
            .HasValue<FirstChicagoValuation>(nameof(FirstChicagoValuation));

        builder.OwnsOne(i => i.Owner);
    }
}

public class ScoringCardValuationMapping : IEntityTypeConfiguration<ScoringCardValuation>
{
    public void Configure(EntityTypeBuilder<ScoringCardValuation> builder)
    {
        builder.HasOne(c => c.Result).WithOne().HasForeignKey<ScoringCardValuationResult>("ValuationResultId");
    }
}

public class FirstChicagoValuationMapping : IEntityTypeConfiguration<FirstChicagoValuation>
{
    public void Configure(EntityTypeBuilder<FirstChicagoValuation> builder)
    {
        .
        .
        .

        builder.HasOne(c => c.Result).WithOne().HasForeignKey<FirstChicagoValuationResult>("ValuationResultId");
    }
}


public class ValuationResultMapping : IEntityTypeConfiguration<ValuationResult>
{
    public void Configure(EntityTypeBuilder<ValuationResult> builder)
    {
        builder.HasKey(c => c.Id);

        builder.HasDiscriminator()
            .HasValue<ScoringCardValuationResult>(nameof(ScoringCardValuationResult))
            .HasValue<FirstChicagoValuationResult>(nameof(FirstChicagoValuationResult));
    }
}

Now, My problem is when I add a migration EF create tow foriegn key for ValuationResult table

I want to have one foriegn key in ValuationResult table. Actually, I want ValuationBase to have a one to one relationship to ValuationResult. It means I just want two tables. Every Valuation drived class must have a ValuationResult that its type is defined in generic.

How can I fix that?

You've declared two different entity relationships (notwithstanding the fact they both inherit from same base). This is why EF is creating two foreign keys.

Unless there's some unseen reason to keep that Generic layer in the inheritance structure, then I'd remove it and put the Result link on the base class.

Again, unless there's some unseen reason to keep the Enum of types (ValuationType) then just use the class type:

typeof(ScoringCardValuation);
// or on an instance:
cardValuation.GetType();

I'd refactor entities to:

public abstract class ValuationBase
{
    public Guid Id { get; internal set; }
    public DateTime CreationDateTime { get; internal set; }

    public Owner Owner { get; internal set; }

    public virtual ValuationResult Result { get; internal set; }
}

public class ScoringCardValuation : ValuationBase
{
    public override ScoringCardValuationResult Result 
    { 
        get => base.Result as ScoringCardValuationResult;
        protected set => base.Result = value; 
    }
}

public class FirstChicagoValuation : Valuation<FirstChicagoValuationResult>
{
    public override FirstChicagoValuationResult Result 
    { 
        get => base.Result as FirstChicagoValuationResult;
        protected set => base.Result = value; 
    }
}

Then configure:

public class ValuationBaseMapping : IEntityTypeConfiguration<ValuationBase>
{
    public void Configure(EntityTypeBuilder<ValuationBase> builder)
    {
        builder.HasKey(c => c.Id);

        builder.HasDiscriminator()
            .HasValue<ScoringCardValuation>(nameof(ScoringCardValuation))
            .HasValue<FirstChicagoValuation>(nameof(FirstChicagoValuation));

        builder.OwnsOne(i => i.Owner);

        builder.HasOne(c => c.Result)
            .WithOne()
            .HasForeignKey<ValuationResult>("ValuationResultId");
    }
}

public class ScoringCardValuationMapping : IEntityTypeConfiguration<ScoringCardValuation>
{
    public void Configure(EntityTypeBuilder<ScoringCardValuation> builder)
    {
    }
}

public class FirstChicagoValuationMapping : IEntityTypeConfiguration<FirstChicagoValuation>
{
    public void Configure(EntityTypeBuilder<FirstChicagoValuation> builder)
    {
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM