hazzik / DelegateDecompiler

A library which is able to decompile a delegate or a method body to its lambda representation
MIT License
522 stars 62 forks source link

Nested computed property support without using [Computed]? #238

Closed alexy4744 closed 1 month ago

alexy4744 commented 1 month ago

I have the following class:

public class Document
{
    public ICollection<Review> Reviews { get; private set; } = [];

    public DateTimeOffset? SubmittedAtUtc { get; private set; }

    public bool IsApproved => Reviews.Any(review => review.IsApproved);

    public bool IsRejected => Reviews.Count > 0 && Reviews.All(review => !review.IsApproved);

    public bool IsSubmitted => SubmittedAtUtc != null;

    public string Status =>
        IsApproved ? "APPROVED" :
        IsSubmitted ? "SUBMITTED" :
        IsRejected ? "REJECTED" :
        "DRAFT";
}

I would like to query it using EF Core like so:

return await context.MeetingNoticeFlyers
            .AsNoTracking()
            .DecompileAsync()
            .Where(x => x.Status.Computed() == dto.Status)
            .ToListAsync();

But this throws a LINQ translation error because .Computed() is not applied to the dependent properties (IsApproved, IsRejected, IsSubmitted). I could solve this by adding [Computed] to the properties in my class, but I don't want to pollute my domain model with it.

Will this be supported in the future? If not, is it possible to write an extension method so that .Computed() will also be applied to the dependent properties?

Thanks!

hazzik commented 1 month ago

Thanks for the suggestion, @alexy4744. How would I know where to stop decompiling methods?

If you do not want to pollute your model with the attributes, you should be able to register computed in mapping by code or by convention.

alexy4744 commented 1 month ago

I'm not familiar with the inner workings and the reflection behind it, but I was thinking maybe something like .Computed(recursive: true), which will recursively decompile any descendant methods.

So in my example, x.Status.Computed(recursive: true) will be decompiled and it sees that it uses IsApproved, IsRejected, IsSubmitted properties, so it will call .Computed(recursive: true) on those properties also. Not sure if this is possible?

If you do not want to pollute your model with the attributes, you should be able to register computed in mapping by code or by convention.

Is this already possible today with the package? I can't find any documentation on it. Thanks!

hazzik commented 1 month ago

Is this already possible today with the package?

Yes

https://github.com/hazzik/DelegateDecompiler/blob/f6df34fadb81a8ce60ad008da24fcc00fb30d0ec/src/DelegateDecompiler.EntityFramework.Tests/EfItems/EfTestDbContext.cs#L70-L74

on those properties also. Not sure if this is possible?

How would it know that it should not decompile SubmittedAtUtc?

Other option, if you do not use EF - you can override Configuration.ShouldDecompile method:

https://github.com/hazzik/DelegateDecompiler/blob/f6df34fadb81a8ce60ad008da24fcc00fb30d0ec/src/DelegateDecompiler/Configuration.cs#L46

And then register your custom configuration like following:

var configuration = new MyCustomConfiguration();
Configuration.Configure(configuration);
alexy4744 commented 1 month ago

https://github.com/hazzik/DelegateDecompiler/blob/f6df34fadb81a8ce60ad008da24fcc00fb30d0ec/src/DelegateDecompiler.EntityFramework.Tests/EfItems/EfTestDbContext.cs#L70-L74

This works even better and makes more sense than what I was suggesting. Thanks!

hazzik commented 1 month ago

Glad I could help. And thanks for the contribution. Really appreciate it.