C
C#2w ago
Tvde1

abstract methods inside EF core queries

I'm trying to model my domain as follows (some EF configuration stuff is left out for brevity)
class Contract
{
public List<PaymentAttempt> PaymentAttempts { get; }
}

abstract class PaymentAttempt // uses tpc
{
public abstract bool HasPaid(int amount);
}

class BankPaymentAttempt : PaymentAttempt
{
public string BankAccount { get; set; }
public int AmountPaid { get; set; }

public bool HasPaid(int amount) => AmountPaid >= amount;
}

class VipUserPaymentAttempt : PaymentAttempt
{
public bool IsSuperVip { get; set; }

public bool HasPaid(int amount) => IsSuperVip;
}
class Contract
{
public List<PaymentAttempt> PaymentAttempts { get; }
}

abstract class PaymentAttempt // uses tpc
{
public abstract bool HasPaid(int amount);
}

class BankPaymentAttempt : PaymentAttempt
{
public string BankAccount { get; set; }
public int AmountPaid { get; set; }

public bool HasPaid(int amount) => AmountPaid >= amount;
}

class VipUserPaymentAttempt : PaymentAttempt
{
public bool IsSuperVip { get; set; }

public bool HasPaid(int amount) => IsSuperVip;
}
I am looking to do a query like:
var outstandingBalance = 123;
context.Contracts
.Where(contract => contract.PaymentAttempts
.Any(attempt => attempt.HasPaid(outstandingBalance)
);
var outstandingBalance = 123;
context.Contracts
.Where(contract => contract.PaymentAttempts
.Any(attempt => attempt.HasPaid(outstandingBalance)
);
EF is not able to translate this query unfortunately Is there a method to handle this? I thought about returning expression trees manually but that seems not very easy
11 Replies
Tvde1
Tvde1OP2w ago
I'm thinking of
class BankPaymentAttempt : PaymentAttempt
{
public Expression<Func<int, bool>> HasPaid = (int amount) => AmountPaid >= amount;
}
class BankPaymentAttempt : PaymentAttempt
{
public Expression<Func<int, bool>> HasPaid = (int amount) => AmountPaid >= amount;
}
but how would I use this inside an any? EF.CompileQuery also wouldn't help me in this case I think I'm trying to refrain from making a complex expression tree soup - even though I know that is possible I wish to receive a query which looks like
SELECT * FROM [Contracts]
LEFT JOIN (
SELECT [pa].[Id], [pa].[ContractId], [e].Property, [e0].OtherProperty CASE
WHEN [e].[Id] IS NOT NULL THEN N'VipUserPaymentAttempt'
WHEN [e0].[Id] IS NOT NULL THEN N'BankPaymentAttempt'
END AS [Discriminator]
FROM [PaymentAttempt] AS [pa]
LEFT JOIN [BankPaymentAttempt] AS [e] ON [pa].[Id] = [e].[Id]
LEFT JOIN [VipUserPaymentAttempt] AS [e0] ON [pa].[Id] = [e0].[Id]
) AS [s] ON [c].[Id] = [s].[ContractId]
WHERE ([e] IS NULL OR [e].Amount > @__input_amount) -- or something
SELECT * FROM [Contracts]
LEFT JOIN (
SELECT [pa].[Id], [pa].[ContractId], [e].Property, [e0].OtherProperty CASE
WHEN [e].[Id] IS NOT NULL THEN N'VipUserPaymentAttempt'
WHEN [e0].[Id] IS NOT NULL THEN N'BankPaymentAttempt'
END AS [Discriminator]
FROM [PaymentAttempt] AS [pa]
LEFT JOIN [BankPaymentAttempt] AS [e] ON [pa].[Id] = [e].[Id]
LEFT JOIN [VipUserPaymentAttempt] AS [e0] ON [pa].[Id] = [e0].[Id]
) AS [s] ON [c].[Id] = [s].[ContractId]
WHERE ([e] IS NULL OR [e].Amount > @__input_amount) -- or something
Unknown User
Unknown User2w ago
Message Not Public
Sign In & Join Server To View
Tvde1
Tvde1OP2w ago
I can use:
var outstandingBalance = 123;
context.Contracts
.WhereIsPaid(outstandingBalance)
.ToListAsync();

public static IQueryable<Contract> WhereIsPaid(this IQueryable<Contract> query, int amount)
{
return query.Where(contract => contract.PaymentAttempts.Any(y =>
(y as BankPaymentAttempt)!.AmountPaid >= amount ||
(y as VipUserPaymentAttempt)!.IsSuperVip)
);
}
var outstandingBalance = 123;
context.Contracts
.WhereIsPaid(outstandingBalance)
.ToListAsync();

public static IQueryable<Contract> WhereIsPaid(this IQueryable<Contract> query, int amount)
{
return query.Where(contract => contract.PaymentAttempts.Any(y =>
(y as BankPaymentAttempt)!.AmountPaid >= amount ||
(y as VipUserPaymentAttempt)!.IsSuperVip)
);
}
Though this defeats the open-closed principle. I would rather have each class implement their own filter expression
Unknown User
Unknown User2w ago
Message Not Public
Sign In & Join Server To View
Tvde1
Tvde1OP2w ago
a predicate is a delegate that is equal to Func<T, bool>..?
Unknown User
Unknown User2w ago
Message Not Public
Sign In & Join Server To View
Tvde1
Tvde1OP2w ago
let me try using the Func<T, bool> instead of an expression - but I'm pretty certain EF will refuse to attempt to translate it
Unknown User
Unknown User2w ago
Message Not Public
Sign In & Join Server To View
Anton
Anton2w ago
you can't translate it ever the closest you can do is having the property return an Expression<Func<...>> the either use a library that will take it into account idk if projectable does this, it might or like you said, construct a tree manually there is no other way and yes, it is quite complicated then you have it run on the client check you queries
Unknown User
Unknown User2w ago
Message Not Public
Sign In & Join Server To View
Anton
Anton2w ago
as sure as one can be ef core doesn't transform functions to sql it only transforms expression trees

Did you find this page helpful?