Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I access the discriminator value in TPH mapping with Entity Framework 4 CTP5

Using Entity Framework 4 CTP5 Code First and this example

Is it possible to access the discriminator value?

I would like to use it in a projection like

context.BillingDetails.Select(x => new { Number = x.Number, DiscrimitatorValue = /* how do I get the discriminator value? */ });

From this post I understand the discriminator cannot be mapped to a property but is there any other way of accessing it?

like image 777
David Glenn Avatar asked Dec 24 '10 11:12

David Glenn


6 Answers

I may be late to the game on this one, but I just added a getter property to the base class that returned the name of the current type:

public string DiscriminatorValue {
    get {
        return this.GetType().Name;
    }
}

Since by default EF is going to use this same value for the Discriminator field, they will match up.

like image 61
Paul Avatar answered Oct 02 '22 15:10

Paul


In EF Core 2.1 (I haven't checked previous versions) it's enough to add Discriminator to the base abstract class as private set property. It will be mapped with adequate value.

public abstract class Entity
{
    public int Id { get; set; }
    public string Discriminator { get; private set; }
}

EF by itself will automatically insert appropriate discriminator value to the database and will automatically set it to an object on read.

like image 26
Michael Black Avatar answered Oct 02 '22 14:10

Michael Black


After further information from Morteza Manavi in the comments of his post the simple answer is no

you should be aware that the discriminator column is used internally by Code First and you cannnot read/write its values from an inheritance mapping standpoint.

To access the discriminator I would have to execute a SqlQuery against the database or change my mapping strategy.

like image 35
David Glenn Avatar answered Oct 02 '22 13:10

David Glenn


Reason aside, I recently ran into the same problem but believe this is still relevant for v4 of the EF Framework.

First, create a view which selects the discriminator value into two columns.

create view dbo.vw_BillingDetail
as
    select BillingDetailId, DiscriminatorValue, DiscriminatorValue as DiscriminatorValue2 from dbo.BillingDetail
go

Secondly, map the view to your entity during context creation:

modelBuilder
    .Entity<BillingDetail>()
    .HasKey(n => n.BillingDetailId)
    .Map(map =>
    {
        map.ToTable("vw_Person");
    })

Thirdly, define your discriminator mapping for your derived class using one of the columns in your view:

.Map<MyDerivedBillingDetail>(map =>
{
    map.Requires("DiscriminatorValue2").HasValue("YourValue");
})

Finally, define a getter and a private setter for the other discriminator column in your view with the DatabaseGenerated annotation set as Computed to prevent EF from updating/inserting for this field:

class BillingDetail
{
    public BillingDetailId { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DiscriminatorValue { get; private set; }
}

You can change the private setter to be protected and set this value explicitly during the construction of your derived entities so that the discriminator has a value prior to being persisted:

class MyDerivedBillingDetail : BillingDetail
{
    public MyDerivedBillingDetail()
    {
        this.DiscriminatorValue = "MyValue";
    }
}
like image 42
user978139 Avatar answered Oct 02 '22 14:10

user978139


To expand on @Michael Black's answer for Entity Framework Core 2.1 (earlier? tested in 2.1.4)

You can use any property name, database field name and data type you want.

Create a property:

[Column("foo_type_id")]
class Foo {
    public FooTypesEnum TypeId {get; set;}
}

Then in your context class with the fluent API via modelBuilder:

modelBuilder.Entity<Foo>(b => {
    b.HasDiscriminator(foo => foo.TypeId)
        .HasValue<SubFooA>(FooTypesEnum.SubFooA)
        .HasValue<SubFooB>(FooTypesEnum.SubFooB);
});

This is really useful if you need to build composable queries that e.g., group on the discriminator, etc.

like image 36
steamer25 Avatar answered Oct 02 '22 13:10

steamer25


Why don't you use the following query instead?

 var q = con.BillingDetails.OfType<BankAccount>().ToList();
like image 23
Devart Avatar answered Oct 02 '22 13:10

Devart