-
Notifications
You must be signed in to change notification settings - Fork 30
Description
I'm attempting to use this with a GQL server library (Hotchocolate). The way it works is it writes a select statement like this:
class User {
public string Id {get; set;}
public List<Contact> Contacts {get; set;}
int? _contactCount;
[Projectable]
public int ContactCount {
get => Contacts.Count;
set =>_contactCount = value;
}
}
Users.Select(u => new User() { Id = u.Id, ContactCount= u.ContactCount })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 preserve ContactsCount.
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.Countand the first part is excluded from the queryget => Project.Default(_contactCount, () => Contacts.Count)whereProject.Defaultis basically a magic method that gets rewritten by the expression builder into justContactsCountbut it'll return the value from_contactCountby 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 incorrectly
The 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 if Contacts 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
[Projectable(nameof(CalculateContactCount))]
public int ContactCount {get; set;}
private int CalculateContactCount() => Contacts.Count;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.