Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using unmapped properties getters in queries #2886

Open
joecarl opened this issue Mar 6, 2024 · 3 comments
Open

Allow using unmapped properties getters in queries #2886

joecarl opened this issue Mar 6, 2024 · 3 comments
Assignees

Comments

@joecarl
Copy link
Contributor

joecarl commented Mar 6, 2024

Imagine the following scenario:

[EntitySet("TestEntities")]
public class TestModel
{
	public decimal PlasticKg { get; set; }
	public decimal OthersKg { get; set; }

	[NotMapped] // This attribute doesn't exist in Microsoft.OData.Client but I think it would be necessary for this case and useful for other cases
	public decimal TotalKg { get => PlasticKg + OthersKg; }	
}


public class MyContext : DataServiceContext
{
	public DataServiceQuery<TestModel> TestEntities;

	public MyContext(Uri serviceRoot) : base(serviceRoot)
	{      
		TestEntities = CreateQuery<TestModel>("TestEntities");
	}
}


// Main program:

var ctx = new MyContext(uri);

// The following lines should behave the same (but they don't):
ctx.TestEntities.Where(c => c.PlasticKg + c.OthersKg > 10).ToList(); // Works fine
ctx.TestEntities.Where(c => c.TotalKg > 10).ToList(); // Throws because the server responds with "Bad Request: Could not find a property named 'TotalKg' in model ..."  

So my proposal is:

  • Implement the NotMappedAttribute class.
  • On query building, if using a property that has a custom getter and the NotMapped attribute, then the getter definition should be used as part of the query instead of using the property name.
  • Additionaly the NotMappedAttribute should prevent the materializer from setting that property in all cases, it also should throw exception if the property is used in a query and it doesn't have a custom getter.

I could implement this myself and submit a pull request, but I would like to receive some feedback before proceding. Thanks

@xuzhg
Copy link
Member

xuzhg commented Mar 12, 2024

@joecarl Thanks for sharing your thoughts. Any contributions are welcome. Feel free to share the PR.

By the way, it looks like to design/implement the $compute feature. For example:

[Computed] 
public decimal TotalKg { get => PlasticKg + OthersKg; }	

Here's our proposal:
If an unmapped property (Not Found from Edm Model at the client side) is defined and used in the LINQ Expression, we can do:

  1. If this property has '[Computed]' decorated, and it only contains the getter, we can generate this property into $compute clause. So

ctx.TestEntities.Where(c => c.TotalKg > 10).ToList(); can be translated to:

~/testEntities?$filter=TotalKey gt 10&$compute=PlasticKg add OthersKg as TotalKey

  1. If this property has nothing decorated, we can translate this property as nested expression as:

~/testEntities?$filter=(PlasticKg add OthersKg) gt 10

Any thoughts?

@joecarl
Copy link
Contributor Author

joecarl commented Mar 13, 2024

@xuzhg Thankyou very much for your answer.
Your proposal seems good to me, I will think about it and will likely submit a pull request. I have a question though: does the $compute feature bring any advantage over the non computed query?

Also, about the NotMapped attribute. It might not be necessary for this case.
But imagine the scenario where my model has a property public decimal Cost { get; set; } and the OData endpoint actually has a field named Cost but for whatever reason I don't want to use it (e.g.: the api has obsolete prices and i want to calculate it myself later or maybe the matching name are just an unfortunate coincidence). How could I prevent the materializer from setting that prop? Also how could I make sure that an exception will be thrown if I use that prop in a query? (An exception should be thrown since otherwise I would be doing an unintended filtering and the api would silently return wrong results)

@joecarl
Copy link
Contributor Author

joecarl commented Mar 18, 2024

I've been attempting to implement this, but I've encountered a significant obstacle. The getter method's body cannot be translated at runtime because it is a compiled method, unlike Expression objects whose tree is stored at compile time, allowing access at runtime.

Does anybody know how to face this problem? Any help is appreciated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants