Closed hahn-kev closed 11 months ago
One other option I could think of is to declare a normal property and declare the expression somewhere else. Like this
This is actually already (partly) supported through the use of UseMemberBody. Though we have no tests in place to see if it workers for properties with getters and setters.
I'll take a closer look at this issue and the associated PR later.
Thanks UseMemberBody
works perfectly for my use case. Here's my model:
[Projectable(UseMemberBody = nameof(InternalUserCount))]
public int UserCount { get; set; }
private static Expression<Func<Project, int>> InternalUserCount => project => project.Users.Count;
When I select UserCount it does exactly what I expect.
A comment on how you declare the private expression, it seems odd that we need to do this:
private static Expression<Func<Project, int>> InternalUserCount => project => project.Users.Count;
instead of just assigning the field a value like this:
private static Expression<Func<Project, int>> InternalUserCount = project => project.Users.Count;
notice there's only 1 =>
, it's a minor thing but it could also be confusing if you did the wrong thing on accident.
Comment noted, and I agree that you should be able to use the body of either a property or a field, feel free to open up a new issue of you feel that's something that should be fixed.
I'm attempting to use this with a GQL server library (Hotchocolate). The way it works is it writes a select statement like this:
later on when the data leaves the API the User is serialized and the values are read from the properties.
this works fine, except when I try to use a projectable since there's no setter. The select statment above won't work for those properties. This is why I worked on #79. However once that gets merged my issue still won't be fixed entirely. The Query will execute just fine, and the value from the db will be stored in the object using the setter on the property. However when the serializer reads ContactCount it won't be able to read the value that was output from the SQL as the code will just execute
Contacts.Count
. I would love it if I could return the value that was set in the private field instead, but still preserveContactsCount
.The only thing I could think of is to write the getter in some way so it'll return the value stored in the field at runtime, but make the generator ignore that part of the code somehow.
get => _contactCount ?? Contacts.Count
and the first part is excluded from the queryget => Project.Default(_contactCount, () => Contacts.Count)
whereProject.Default
is basically a magic method that gets rewritten by the expression builder into justContactsCount
but it'll return the value from_contactCount
by default.get => Project.Default(_contactCount, u => u.Contacts.Count)
would work without performance issues because it could be a static method without a capture, but it could be really confusing, and users could write it incorrectlyThe first one is pretty clean, but it would be hard to write the parser side as sometimes you might actually want that whole thing to be the SQL. It might also be confusing for anyone to read the actual code. The second option could be easier to understand and write the parser for, but it might actually have really bad performance as a new lambda would have to be created each time the getter was called. We can't just pass the value in directly as
Contacts.Count
could throw ifContacts
is null (or any other case also).One other option I could think of is to declare a normal property and declare the expression somewhere else. Like this
This would solve any of the weird issues seen above and might be the best option, but I'm not sure how much work it would be to support this, maybe none, I'm not sure.
I know this is a pretty out there use case but I'd like to figure something out so I can use this together with GQL.