EntityFrameworkCore.Projectables icon indicating copy to clipboard operation
EntityFrameworkCore.Projectables copied to clipboard

Overriden property not getting used

Open honzapatCZ opened this issue 1 year ago • 6 comments

Hi I am having a base type and a few derived types of it.

[Table("JournalEntryRefs")]
public abstract class JournalEntryRef
{
    [Projectable]
    public virtual string? DisplayName => null;
}

[Table("JournalEntryRefs")]
public class InvoiceJournalEntryRef : JournalEntryRef
{
    [Projectable]
    public override string? DisplayName => Invoice.Number != null ? Invoice.Number : "";
}

[Table("JournalEntryRefs")]
public class PaymentJournalEntryRef : JournalEntryRef
{
    [Projectable]
    public override string? DisplayName => Payment.Number;
}

When I query all the JournalEntryRef's(ie a collection somewhere) I specify that I want the reference column(beside others) and I'd expect that as such it would return the Invoice number for Invoice JournalEntryRef and Payment number for PaymentEntryRef. But currently they are always null. Is that a bug? If so how can we fix it? Or is that entirely not possible?

honzapatCZ avatar May 25 '23 10:05 honzapatCZ

Can you try and use methods instead of properties and see if the issue persists? We have done work to support calling derived projectable methods, I'm not sure if this work also translated to properties.

Looking forward to hearing back.

koenbeuk avatar May 25 '23 23:05 koenbeuk

Can you try and use methods instead of properties and see if the issue persists? We have done work to support calling derived projectable methods, I'm not sure if this work also translated to properties.

Looking forward to hearing back.

Still the same

    [Table("JournalEntryRefs")]
    public abstract class JournalEntryRef
    {
        [Projectable]
        public string? DisplayName => GetDisplayName();

        [Projectable]
        public virtual string? GetDisplayName() => null;
    }

    [Table("JournalEntryRefs")]
    public class InvoiceJournalEntryRef : JournalEntryRef
    {
        [Projectable]
        public override string? GetDisplayName() => Invoice.Number != null ? Invoice.Number : "";
    }

    [Table("JournalEntryRefs")]
    public class PaymentJournalEntryRef : JournalEntryRef
    {
        [Projectable]
        public override string? GetDisplayName() => Payment.Number;
    }

    [Table("JournalEntryRefs")]
    public class BankEntryJournalEntryRef : JournalEntryRef
    {
        [Projectable]
        public override string? GetDisplayName() => "BE";
    }

Tho to be 100% exact Iam accessing it yet from another class(JournalEntries) as Projectable

[Projectable]
public string? Reference => JournalEntryRef != null ? JournalEntryRef.DisplayName : null;

EDIT: Here's the SQL when querying for all the Journal Entries, it seems the the if case is getting generated incorrectly.

SELECT `j`.`Id`, `j0`.`AccountNo`, `j0`.`CompanyId`, `j0`.`ActiveFrom`, `j0`.`ActiveTo`, `j0`.`CreatedAt`, `j0`.`DeletedAt`, `j0`.`Description`, `j0`.`Type`, `j0`.`UpdatedAt`, `j1`.`AccountNo`, `j1`.`CompanyId`, `j1`.`ActiveFrom`, `j1`.`ActiveTo`, `j1`.`CreatedAt`, `j1`.`DeletedAt`, `j1`.`Description`, `j1`.`Type`, `j1`.`UpdatedAt`, CASE
          WHEN `j2`.`Id` IS NOT NULL THEN NULL
          ELSE NULL
      END, `j`.`Description`, `j`.`AccountReceivableNo`, `j`.`AccountPayableNo`, `j`.`Amount`, `j`.`Date`
      FROM `JournalEntries` AS `j`
      INNER JOIN `JournalAccounts` AS `j0` ON (`j`.`AccountReceivableNo` = `j0`.`AccountNo`) AND (`j`.`CompanyId` = `j0`.`CompanyId`)
      INNER JOIN `JournalAccounts` AS `j1` ON (`j`.`AccountPayableNo` = `j1`.`AccountNo`) AND (`j`.`CompanyId` = `j1`.`CompanyId`)
      LEFT JOIN `JournalEntryRefs` AS `j2` ON `j`.`Id` = `j2`.`JournalEntryId`
      WHERE `j`.`CompanyId` = @__p_0
      ORDER BY `j`.`Date` DESC

honzapatCZ avatar May 28 '23 18:05 honzapatCZ

Tho to be 100% exact Iam accessing it yet from another class(JournalEntries) as Projectable

There is a problem, during the query compilation phase, we do not know what JournalEntryRef is except for that its JournalEntryRef. So the only sensible thing it can do is project JournalEntryRef.GetDisplayName. Only when the query executes can it resolve what JournalEntryRef actually is.

You'll have to come up with something creative in order to bind to the correct implementation during query execution, e.g.

public string? Reference => JournalEntryRef is InvoiceJournalEntryRef ? ((InvoiceJournalEntryRef)JournalEntryRef).GetDisplayName() :  JournalEntryRef is PaymentJournalEntryRef ? ((PaymentJournalEntryRef )JournalEntryRef).GetDisplayName() : JournalEntryRef is BankEntryJournalEntryRef ? ((BankEntryJournalEntryRef )JournalEntryRef).GetDisplayName() : null;

koenbeuk avatar Jun 03 '23 19:06 koenbeuk

Tho to be 100% exact Iam accessing it yet from another class(JournalEntries) as Projectable

There is a problem, during the query compilation phase, we do not know what JournalEntryRef is except for that its JournalEntryRef. So the only sensible thing it can do is project JournalEntryRef.GetDisplayName. Only when the query executes can it resolve what JournalEntryRef actually is.

You'll have to come up with something creative in order to bind to the correct implementation during query execution, e.g.

public string? Reference => JournalEntryRef is InvoiceJournalEntryRef ? ((InvoiceJournalEntryRef)JournalEntryRef).GetDisplayName() :  JournalEntryRef is PaymentJournalEntryRef ? ((PaymentJournalEntryRef )JournalEntryRef).GetDisplayName() : JournalEntryRef is BankEntryJournalEntryRef ? ((BankEntryJournalEntryRef )JournalEntryRef).GetDisplayName() : null;

And can't exactly this be generated by Projectables? Ie: If we know it's virtual the actual implementation will looks thru what inherits this class and generates the ifs?

honzapatCZ avatar Jun 03 '23 21:06 honzapatCZ

Ie: If we know it's virtual the actual implementation will looks thru what inherits this class and generates the ifs?

Technically, yes. However that is a big ask. This would need substantial design. Feel free to propose something but at the moment, this would not be supported :)

koenbeuk avatar Jun 04 '23 01:06 koenbeuk

Ie: If we know it's virtual the actual implementation will looks thru what inherits this class and generates the ifs?

Technically, yes. However that is a big ask. This would need substantial design. Feel free to propose something but at the moment, this would not be supported :)

Ok, Ill look into it

honzapatCZ avatar Jun 04 '23 20:06 honzapatCZ