You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
My Entity QualityDataPart implements an interface IQualityDataPart which has a property Type (string).
In order to save storage this text is stored in a different Entity QualityDataPartInfo
Now I want to map the property from the different Entity:
Having a projectable property on the concrete entity type as you have in QualityDataPart is supported. Your sample code is therefore supported and when I run this:
Ok I have found the generated sources (maybe you should add a hint to the readme that they are visible in Dependencies > Analyzers > EntityFrameworkCore.Projectables.Generator)
This seems correct to me. But I think the expession from the interface is just not translated to the concrete type.
When I build the expression with the concrete type everything works as expected. But when I use the interface the auto-generated expression is not used.
//working as expectedExpression<Func<QualityDataPart,bool>>test= p =>p.Type=="Test";vartestResult=awaitqualityDataParts.Where(test).ToListAsync();//generated expression not used -> "Translation of member 'Type' on entity type 'QualityDataPart' failed"Expression<Func<IQualityDataPart,bool>>test2= p =>p.Type=="Test";vartestResult2=awaitqualityDataParts.Where(test2).ToListAsync();
This seems to be an issue that will hard to solve.
Expression<Func<IQualityDataPart, bool>> test = p => p.Type == "Test"; doesn't inform this library what concrete type IQualityDataPart actually is and therefore, this library would not know where to get the generated expression from. I can see some options here:
Support for default interface implementation so that you can mark an interface member as Projectable and give it a default implementation for which we can generate an expression and use it at runtime.
Enhance the projectable attribute to refer to an different type when specifying: UseMemberBody so that we can teach an interface where the expression of that interface lives.
I have not tested this with your code, but we had a similar situation with a slightly stupid - but working - solution. Maybe this helps others:
Not working (from your code):
Expression<Func<IQualityDataPart,bool>>test2= p =>p.Type=="Test";vartestResult2=awaitqualityDataParts.Where(test2).ToListAsync();
Working (in theory):
publicstringGetData(IQualityDataPartinput){returninput.Type}Expression<Func<IQualityDataPart,bool>>test2= p =>GetData(p)=="Test";vartestResult2=awaitqualityDataParts.Where(test2).ToListAsync();
My understanding of this solution: The main issue with Interfaces is, that the library tries to find a class when it only got information to find an interface - which is why it fails originally. What we do, is give it a method whithout having to touch the interface. The method is outside the generated expression. So it actually knows how to deal with the interface.
I do not have a 100% grasp of this problem, so our case might be different than this. If so, I apologize. But nevertheless, this issue helped solve our problem, so thank you :D
@JodliDev This will not work. In your example, The implementation of GetData is still a blackbox for EF and since the method is not marked as a Projectable, there are no companion expression trees that this library can take to tell EF how it is implemented. Even if that methods was marked as a Projectable, we would still run into this particular issue as we do not know in advance the concrete type of input and therefore we can't swap out a call to Type with a concrete expression.
namespaceEntityFrameworkCore.Projectables.Generated{[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]staticclassBlueDanubeCrmModel_SlModel_IdHelperJoined{staticglobal::System.Linq.Expressions.Expression<global::System.Func<global::BlueDanubeCrmModel.SlModel,global::DataAccessNfStandard.DataExtender.IDataContextForIdHelper,string>>Expression(){return(global::BlueDanubeCrmModel.SlModel@this,global::DataAccessNfStandard.DataExtender.IDataContextForIdHelperdataContext)=>global::BlueDanubeCrmModel.SlModel.BuildIdHelperString(global::System.Linq.Queryable.FirstOrDefault(global::System.Linq.Queryable.Select(global::System.Linq.Queryable.Where(global::BlueDanubeCrmModel.SlModel.GetTable(dataContext), x =>x.Id==@this.RobotModelId), x =>newglobal::BlueDanubeCrmModel.RobotModel.RobotModelIdHelperData{RobMan=x.RobMan,RobMod1=x.RobMod1,RobMod2=x.RobMod2,RobMod3=x.RobMod3})),@this.Category,@this.SlName,@this.Flag,@this.P);}}}
Not the most pretty, and we are still testing things, but so far it seems to work and seems to produce SQL queries that do what we want
Activity
koenbeuk commentedon Oct 31, 2023
Having a projectable property on the concrete entity type as you have in QualityDataPart is supported. Your sample code is therefore supported and when I run this:
I get the following generated companion expression:
Can you share the actual generated code that is failing?
DG4ever commentedon Oct 31, 2023
Thanks for your response. I didn't know that there will be auto generated code.
Where do I find it?
DG4ever commentedon Nov 13, 2023
Ok I have found the generated sources (maybe you should add a hint to the readme that they are visible in Dependencies > Analyzers > EntityFrameworkCore.Projectables.Generator)
Here is the auto generated code:
This seems correct to me. But I think the expession from the interface is just not translated to the concrete type.
When I build the expression with the concrete type everything works as expected. But when I use the interface the auto-generated expression is not used.
koenbeuk commentedon Nov 19, 2023
This seems to be an issue that will hard to solve.
Expression<Func<IQualityDataPart, bool>> test = p => p.Type == "Test";doesn't inform this library what concrete typeIQualityDataPartactually is and therefore, this library would not know where to get the generated expression from. I can see some options here:UseMemberBodyso that we can teach an interface where the expression of that interface lives.JodliDev commentedon Mar 8, 2024
I have not tested this with your code, but we had a similar situation with a slightly stupid - but working - solution. Maybe this helps others:
Not working (from your code):
Working (in theory):
My understanding of this solution: The main issue with Interfaces is, that the library tries to find a class when it only got information to find an interface - which is why it fails originally. What we do, is give it a method whithout having to touch the interface. The method is outside the generated expression. So it actually knows how to deal with the interface.
I do not have a 100% grasp of this problem, so our case might be different than this. If so, I apologize. But nevertheless, this issue helped solve our problem, so thank you :D
koenbeuk commentedon Mar 12, 2024
@JodliDev This will not work. In your example, The implementation of
GetDatais still a blackbox for EF and since the method is not marked as aProjectable, there are no companion expression trees that this library can take to tell EF how it is implemented. Even if that methods was marked as a Projectable, we would still run into this particular issue as we do not know in advance the concrete type of input and therefore we can't swap out a call to Type with a concrete expression.JodliDev commentedon Mar 12, 2024
Hm. But I think it works if
GetData()is static, no?Here is some code that actually works for us (notice
IDataContextForIdHelperthat is "translated" byGetTableinto anIQueryable):This generates the following code:
Not the most pretty, and we are still testing things, but so far it seems to work and seems to produce SQL queries that do what we want