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

Filter properties of a derived class on a list of base class objects #452

Closed
StefanKoenigMUC opened this issue Nov 12, 2020 · 9 comments · Fixed by #509
Closed

Filter properties of a derived class on a list of base class objects #452

StefanKoenigMUC opened this issue Nov 12, 2020 · 9 comments · Fixed by #509
Assignees
Labels

Comments

@StefanKoenigMUC
Copy link

StefanKoenigMUC commented Nov 12, 2020

Hi all,

the situation is as follows:

public class A {}
public class B : A { 
public string MyProperty {get;set;}
}


ParameterExpression transaction = Expression.Parameter(typeof(A), "obj");
LambdaExpression eView = DynamicExpressionParser.ParseLambda(new ParameterExpression[] { LIST_OF_As_and_Bs}, typeof(bool), "obj.MyProperty = "asdf"");
var expressionView = eView.Compile();

unfortunately and somehow expected, this is not working - as A does obviously not contain "MyProperty" and during expression generation, an exception is thrown

System.Linq.Dynamic.Core.Exceptions.ParseException: 'No property or field 'MyProperty' exists in type 'A''
Is there any way available to build this expression in a way, that subclasses of A are also taken into consideration?

Thanks in advance for your help,

@StefanKoenigMUC StefanKoenigMUC changed the title Filter properties of a derived class on a list of base class object Filter properties of a derived class on a list of base class objects Nov 12, 2020
@zspitz
Copy link

zspitz commented Nov 12, 2020

You could cast to B:

B(it).MyProperty

which corresponds to the compiled lambda syntax:

(A x) => ((B)x).MyProperty

@StefanKoenigMUC
Copy link
Author

unfortunately, this won't out for me, as the provided expression is from an extern (user-generated) source, they should not bother about any casting issues.

Aside from that, there is not only a B (there are approx 10 classes, which may be addressed) and only a few of them may have the property.

My "workaround" solution could be some kind of a view class, containing a union of properties from those subclasses and convert them into around. But that doesn't sound like a very good architectural approach.

@zspitz
Copy link

zspitz commented Nov 12, 2020

unfortunately, this won't out for me, as the provided expression is from an extern (user-generated) source, they should not bother about any casting issues.

Do you mean the string is generated somewhere else? How does this external location know that there is a MyProperty? Right before MyProperty is appended to the string, wrap the instance in the appropriate conversion.

If you are producing the string from other input, then why can't you do the same?

Aside from that, there is not only a B (there are approx 10 classes, which may be addressed) and only a few of them may have the property.

Each property you're trying to use has an associated declared type. If that declared type is not the same as the input type, then in order to read the property you'll have to cast first.

My "workaround" solution could be some kind of a view class, containing a union of properties from those subclasses and convert them into around. But that doesn't sound like a very good architectural approach.

Note that you don't have to create a dedicated class for this; Dynamic LINQ allows you to produce a dynamic object:

new ( B(it).MyProperty, C(it).MyProperty1, D(it).MyProperty2 )

and so one. You can create a dynamic object which has all the needed properties, as long as you know that the current element type can be casted to all these types.

@StefanKoenigMUC
Copy link
Author

the source of this string is (kind of) a data-grid, containing the union of properties as columns (each row is an object, not available columns are just empty/nulled). The 3rd party knows about those properties by convention (it's basically manual user input - but they should not care about casting & stuff).

Data structure is kind of

BaseClass > A > B > C
A > D > E
D > F
(> shows inheritance)

for the datagrid I only receive a List.

If a user filters a column, only objects containing this property should be taken into consideration.

@zspitz
Copy link

zspitz commented Nov 13, 2020

Assuming you know, or can determine, that MyProperty belongs to B, and you don't need properties from multiple types in the same expression, you could do this:

var qry = lst.AsQueryable().OfType(typeof(B)).Select("MyProperty");

According to the documentation, this should work:

var lst = new List<A>() {
    new A(),
    new B()
};
var qry = lst.AsQueryable().Where($"np(as(\"{typeof(B).FullName}\").MyProperty) = \"asdf\"");

as is the equivalent of the C# as keyword -- if the it is of type B, it'll return the it; otherwise it will return null.
np is the equivalent of the C# ?. -- if the subject is null, the property read will return null as well.

However, this fails with:

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

@zspitz
Copy link

zspitz commented Nov 15, 2020

Ping @StefanKoenigMUC @StefH @JonathanMagnan -- see previous comment.

@StefanKoenigMUC
Copy link
Author

sorry, didnt get an email / didnt see your last comment.

Thanks a lot, I'll play around a bit further today!

@StefH StefH self-assigned this May 11, 2021
@StefH StefH added the bug label May 11, 2021
@StefH
Copy link
Collaborator

StefH commented May 11, 2021

@StefanKoenigMUC and @zspitz
The bug with IndexOutOfRangeException has been fixed and will be included in next release.

@StefH StefH closed this as completed May 11, 2021
@chait-01
Copy link

I have a similar requirement, and I am using dynamic lambda expression, and trying to filter records based on a derived type.

var expr1 = DynamicExpressionParser.ParseLambda<Settings, bool>(new ParsingConfig(), true, "c => (c.Applicability As WinApplicability).SKUs.Contains(@0)", result);

Here Appilicability is a property within Settings type, and WinApplicability is a derived type of Applicability. SKUs is a property within WinApplicability.

The above line of code fails with the below error. Any idea how to deal with this issue?

Exception
System.Linq.Dynamic.Core.Exceptions.ParseException: ')' or operator expected

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

Successfully merging a pull request may close this issue.

4 participants