Get derived class property from parent without repetitive casting [Solved]
up vote
1
down vote
favorite
I am trying to work-around an annoyance, caused by design failure in the data model structure. Refactoring is not an option, because EF goes crazy. ASP.NET 4.6 framework.
The structure is as follows:
class Course
// properties defining a Course object. Example: Marketing course
public string Name get; set;
class CourseInstance
// properties that define an Instance of course. Example: Marketing course, January
public DateTime StartDate get; set;
class InternalCourseInstance : CourseInstance
// Additional business logic properties. Example : Entry course - Marketing program
public bool IsEntry get; set;
public int CourseId get; set;
public Course Course get; set;
class OpenCourseInstance : CourseInstance
// Separate branch of instance. Example - Marketing course instance
public int Price get; set;
public int CourseId get; set;
public Course Course get; set;
I bet you can already see the flaw? Indeed, for an unknown reason, someone decided to put CourseId
and its navigational property on the derived types, instead of parent. Now every time I want to access the Course
from CourseInstance
, I have do do something like:
x.course => courseInstance is InternalCourseInstance
? (courseInstance as InternalCourseInstance).Course
: (courseInstance as OpenCourseInstance).Course;
You can see how this can become really ugly with several more course instance types that derive from CourseInstance
.
I am looking for a way to short-hand that, essentially create a method or expression which does it internally. There is one more problem however - it has to be translatable to SQL, since more often then not this casting is used on IQueryable
.
The closest I came to the solution is:
// CourseInstance.cs
public static Expression<Func<CourseInstance, Course>> GetCourseExpression =>
t => t is OpenCourseInstance
? (t as OpenCourseInstance).Course
: (t as InternalCrouseInstance).Course
This should work, however sometimes I need Id
or Name
of Course
. And there is no way, as far as I can tell - to expand this Expression in specific circumstances to return Id
or Name
.
I can easily do it inside a method, but then it fails on LINQ to Entities, understandably.
I know it's a project-specific problem, however at this stage it cannot be fixed, so I am trying to find a decent work around.
Solution
Firstly, thanks to HimBromBeere for his answer and patience. I couldn't get his generic overload to work, in my case it was throwing as you might see in the discussion below his answer. Here is how I solved it eventually:
CourseInstance.cs
public static Expression<Func<CourseInstance, TProperty> GetCourseProperty<TProperty>(
Expression<Func<Course, TProperty>> propertySelector)
{
var parameter = Expression.Parameter(typeof(CourseInstance), "ci");
var isInternalCourseInstance = Expression.TypeIs(parameter, typeof(InternalCourseInstance);
// 1) Cast to InternalCourseInstance and get Course property
var getInternalCourseInstanceCourse = Expression.MakeMemberAccess(
Expression.TypeAs(parameter, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var propertyName = ((MemberExpression)propertySelector.Body).Member.Name;
// 2) Get value of <propertyName> in <Course> object.
var getInternalCourseInstanceProperty = Expression.MakeMemberAccess(
getInternalCourseInstanceCourse, typeof(Course).GetProperty(propertyName);
// Repeat steps 1) and 2) for OpenCourseInstance ...
var expression = Expression.Condition(isInternalCourseInstance, getInternalCourseInstanceProperty, getOpenCourseInstanceProperty);
return Expression.Lambda<Func<CourseInstance, TProperty(expression, parameter);
Usage
// his first suggestion - it works, retrieving the `Course` property of `CourseInstance`
var courses = courseInstancesQuery.Select(GetCourse())
// My modified overload above.
var courseNames = courseInstancesQuery.Select(GetCourseProperty<string>(c => c.Name));
Thoughts
The problem with the suggested implementation in my opinion is within the Expression.Call
line. Per MS docs:
Creates a MethodCallExpression that represents a call to a method that takes arguments.
However my desired expression contains no method calls - so I removed it and it worked. Now I simply use the delegate to extract the desired property's name and get that with another MemberAccessExpression
.
This is only my interpretation though. Happy to get corrected, if I am wrong.
Remarks: I recommend caching the typeof
calls in private fields, instead of calling them every time you build the expression. Also this can work for more then two derived classes (in my case InternalCourseInstance
and OpenCourseInstance
), you just need an extra ConditionalExpression
(s).
Edit
I've edited the code section - it seems Expression.Convert
is not supported by EntityFramework, however Expression.TypeAs
works just the same.
c#
add a comment |
up vote
1
down vote
favorite
I am trying to work-around an annoyance, caused by design failure in the data model structure. Refactoring is not an option, because EF goes crazy. ASP.NET 4.6 framework.
The structure is as follows:
class Course
// properties defining a Course object. Example: Marketing course
public string Name get; set;
class CourseInstance
// properties that define an Instance of course. Example: Marketing course, January
public DateTime StartDate get; set;
class InternalCourseInstance : CourseInstance
// Additional business logic properties. Example : Entry course - Marketing program
public bool IsEntry get; set;
public int CourseId get; set;
public Course Course get; set;
class OpenCourseInstance : CourseInstance
// Separate branch of instance. Example - Marketing course instance
public int Price get; set;
public int CourseId get; set;
public Course Course get; set;
I bet you can already see the flaw? Indeed, for an unknown reason, someone decided to put CourseId
and its navigational property on the derived types, instead of parent. Now every time I want to access the Course
from CourseInstance
, I have do do something like:
x.course => courseInstance is InternalCourseInstance
? (courseInstance as InternalCourseInstance).Course
: (courseInstance as OpenCourseInstance).Course;
You can see how this can become really ugly with several more course instance types that derive from CourseInstance
.
I am looking for a way to short-hand that, essentially create a method or expression which does it internally. There is one more problem however - it has to be translatable to SQL, since more often then not this casting is used on IQueryable
.
The closest I came to the solution is:
// CourseInstance.cs
public static Expression<Func<CourseInstance, Course>> GetCourseExpression =>
t => t is OpenCourseInstance
? (t as OpenCourseInstance).Course
: (t as InternalCrouseInstance).Course
This should work, however sometimes I need Id
or Name
of Course
. And there is no way, as far as I can tell - to expand this Expression in specific circumstances to return Id
or Name
.
I can easily do it inside a method, but then it fails on LINQ to Entities, understandably.
I know it's a project-specific problem, however at this stage it cannot be fixed, so I am trying to find a decent work around.
Solution
Firstly, thanks to HimBromBeere for his answer and patience. I couldn't get his generic overload to work, in my case it was throwing as you might see in the discussion below his answer. Here is how I solved it eventually:
CourseInstance.cs
public static Expression<Func<CourseInstance, TProperty> GetCourseProperty<TProperty>(
Expression<Func<Course, TProperty>> propertySelector)
{
var parameter = Expression.Parameter(typeof(CourseInstance), "ci");
var isInternalCourseInstance = Expression.TypeIs(parameter, typeof(InternalCourseInstance);
// 1) Cast to InternalCourseInstance and get Course property
var getInternalCourseInstanceCourse = Expression.MakeMemberAccess(
Expression.TypeAs(parameter, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var propertyName = ((MemberExpression)propertySelector.Body).Member.Name;
// 2) Get value of <propertyName> in <Course> object.
var getInternalCourseInstanceProperty = Expression.MakeMemberAccess(
getInternalCourseInstanceCourse, typeof(Course).GetProperty(propertyName);
// Repeat steps 1) and 2) for OpenCourseInstance ...
var expression = Expression.Condition(isInternalCourseInstance, getInternalCourseInstanceProperty, getOpenCourseInstanceProperty);
return Expression.Lambda<Func<CourseInstance, TProperty(expression, parameter);
Usage
// his first suggestion - it works, retrieving the `Course` property of `CourseInstance`
var courses = courseInstancesQuery.Select(GetCourse())
// My modified overload above.
var courseNames = courseInstancesQuery.Select(GetCourseProperty<string>(c => c.Name));
Thoughts
The problem with the suggested implementation in my opinion is within the Expression.Call
line. Per MS docs:
Creates a MethodCallExpression that represents a call to a method that takes arguments.
However my desired expression contains no method calls - so I removed it and it worked. Now I simply use the delegate to extract the desired property's name and get that with another MemberAccessExpression
.
This is only my interpretation though. Happy to get corrected, if I am wrong.
Remarks: I recommend caching the typeof
calls in private fields, instead of calling them every time you build the expression. Also this can work for more then two derived classes (in my case InternalCourseInstance
and OpenCourseInstance
), you just need an extra ConditionalExpression
(s).
Edit
I've edited the code section - it seems Expression.Convert
is not supported by EntityFramework, however Expression.TypeAs
works just the same.
c#
I´m not sure if this works for EF, but could you go fordynamic
turning your expression into something likeExpression<Func<dynamic, Course>>
? Not ideal, but as your design is broken anyway. In fact I hate myself for even suggesting it...
– HimBromBeere
Nov 9 at 13:15
@HimBromBeere I thought of that, however it does not solve the problem on conceptual level. I still don't have a way of getting theId
ofCourse
, if I need. And more often then not this is required inLINQ expressions and AutoMapper configurations, which means I cannot simply get theId
with follow upSelect
statement, for example. Please don't have yourself, I've expressed enough hate on this subject already :)
– Alex
Nov 9 at 14:21
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I am trying to work-around an annoyance, caused by design failure in the data model structure. Refactoring is not an option, because EF goes crazy. ASP.NET 4.6 framework.
The structure is as follows:
class Course
// properties defining a Course object. Example: Marketing course
public string Name get; set;
class CourseInstance
// properties that define an Instance of course. Example: Marketing course, January
public DateTime StartDate get; set;
class InternalCourseInstance : CourseInstance
// Additional business logic properties. Example : Entry course - Marketing program
public bool IsEntry get; set;
public int CourseId get; set;
public Course Course get; set;
class OpenCourseInstance : CourseInstance
// Separate branch of instance. Example - Marketing course instance
public int Price get; set;
public int CourseId get; set;
public Course Course get; set;
I bet you can already see the flaw? Indeed, for an unknown reason, someone decided to put CourseId
and its navigational property on the derived types, instead of parent. Now every time I want to access the Course
from CourseInstance
, I have do do something like:
x.course => courseInstance is InternalCourseInstance
? (courseInstance as InternalCourseInstance).Course
: (courseInstance as OpenCourseInstance).Course;
You can see how this can become really ugly with several more course instance types that derive from CourseInstance
.
I am looking for a way to short-hand that, essentially create a method or expression which does it internally. There is one more problem however - it has to be translatable to SQL, since more often then not this casting is used on IQueryable
.
The closest I came to the solution is:
// CourseInstance.cs
public static Expression<Func<CourseInstance, Course>> GetCourseExpression =>
t => t is OpenCourseInstance
? (t as OpenCourseInstance).Course
: (t as InternalCrouseInstance).Course
This should work, however sometimes I need Id
or Name
of Course
. And there is no way, as far as I can tell - to expand this Expression in specific circumstances to return Id
or Name
.
I can easily do it inside a method, but then it fails on LINQ to Entities, understandably.
I know it's a project-specific problem, however at this stage it cannot be fixed, so I am trying to find a decent work around.
Solution
Firstly, thanks to HimBromBeere for his answer and patience. I couldn't get his generic overload to work, in my case it was throwing as you might see in the discussion below his answer. Here is how I solved it eventually:
CourseInstance.cs
public static Expression<Func<CourseInstance, TProperty> GetCourseProperty<TProperty>(
Expression<Func<Course, TProperty>> propertySelector)
{
var parameter = Expression.Parameter(typeof(CourseInstance), "ci");
var isInternalCourseInstance = Expression.TypeIs(parameter, typeof(InternalCourseInstance);
// 1) Cast to InternalCourseInstance and get Course property
var getInternalCourseInstanceCourse = Expression.MakeMemberAccess(
Expression.TypeAs(parameter, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var propertyName = ((MemberExpression)propertySelector.Body).Member.Name;
// 2) Get value of <propertyName> in <Course> object.
var getInternalCourseInstanceProperty = Expression.MakeMemberAccess(
getInternalCourseInstanceCourse, typeof(Course).GetProperty(propertyName);
// Repeat steps 1) and 2) for OpenCourseInstance ...
var expression = Expression.Condition(isInternalCourseInstance, getInternalCourseInstanceProperty, getOpenCourseInstanceProperty);
return Expression.Lambda<Func<CourseInstance, TProperty(expression, parameter);
Usage
// his first suggestion - it works, retrieving the `Course` property of `CourseInstance`
var courses = courseInstancesQuery.Select(GetCourse())
// My modified overload above.
var courseNames = courseInstancesQuery.Select(GetCourseProperty<string>(c => c.Name));
Thoughts
The problem with the suggested implementation in my opinion is within the Expression.Call
line. Per MS docs:
Creates a MethodCallExpression that represents a call to a method that takes arguments.
However my desired expression contains no method calls - so I removed it and it worked. Now I simply use the delegate to extract the desired property's name and get that with another MemberAccessExpression
.
This is only my interpretation though. Happy to get corrected, if I am wrong.
Remarks: I recommend caching the typeof
calls in private fields, instead of calling them every time you build the expression. Also this can work for more then two derived classes (in my case InternalCourseInstance
and OpenCourseInstance
), you just need an extra ConditionalExpression
(s).
Edit
I've edited the code section - it seems Expression.Convert
is not supported by EntityFramework, however Expression.TypeAs
works just the same.
c#
I am trying to work-around an annoyance, caused by design failure in the data model structure. Refactoring is not an option, because EF goes crazy. ASP.NET 4.6 framework.
The structure is as follows:
class Course
// properties defining a Course object. Example: Marketing course
public string Name get; set;
class CourseInstance
// properties that define an Instance of course. Example: Marketing course, January
public DateTime StartDate get; set;
class InternalCourseInstance : CourseInstance
// Additional business logic properties. Example : Entry course - Marketing program
public bool IsEntry get; set;
public int CourseId get; set;
public Course Course get; set;
class OpenCourseInstance : CourseInstance
// Separate branch of instance. Example - Marketing course instance
public int Price get; set;
public int CourseId get; set;
public Course Course get; set;
I bet you can already see the flaw? Indeed, for an unknown reason, someone decided to put CourseId
and its navigational property on the derived types, instead of parent. Now every time I want to access the Course
from CourseInstance
, I have do do something like:
x.course => courseInstance is InternalCourseInstance
? (courseInstance as InternalCourseInstance).Course
: (courseInstance as OpenCourseInstance).Course;
You can see how this can become really ugly with several more course instance types that derive from CourseInstance
.
I am looking for a way to short-hand that, essentially create a method or expression which does it internally. There is one more problem however - it has to be translatable to SQL, since more often then not this casting is used on IQueryable
.
The closest I came to the solution is:
// CourseInstance.cs
public static Expression<Func<CourseInstance, Course>> GetCourseExpression =>
t => t is OpenCourseInstance
? (t as OpenCourseInstance).Course
: (t as InternalCrouseInstance).Course
This should work, however sometimes I need Id
or Name
of Course
. And there is no way, as far as I can tell - to expand this Expression in specific circumstances to return Id
or Name
.
I can easily do it inside a method, but then it fails on LINQ to Entities, understandably.
I know it's a project-specific problem, however at this stage it cannot be fixed, so I am trying to find a decent work around.
Solution
Firstly, thanks to HimBromBeere for his answer and patience. I couldn't get his generic overload to work, in my case it was throwing as you might see in the discussion below his answer. Here is how I solved it eventually:
CourseInstance.cs
public static Expression<Func<CourseInstance, TProperty> GetCourseProperty<TProperty>(
Expression<Func<Course, TProperty>> propertySelector)
{
var parameter = Expression.Parameter(typeof(CourseInstance), "ci");
var isInternalCourseInstance = Expression.TypeIs(parameter, typeof(InternalCourseInstance);
// 1) Cast to InternalCourseInstance and get Course property
var getInternalCourseInstanceCourse = Expression.MakeMemberAccess(
Expression.TypeAs(parameter, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var propertyName = ((MemberExpression)propertySelector.Body).Member.Name;
// 2) Get value of <propertyName> in <Course> object.
var getInternalCourseInstanceProperty = Expression.MakeMemberAccess(
getInternalCourseInstanceCourse, typeof(Course).GetProperty(propertyName);
// Repeat steps 1) and 2) for OpenCourseInstance ...
var expression = Expression.Condition(isInternalCourseInstance, getInternalCourseInstanceProperty, getOpenCourseInstanceProperty);
return Expression.Lambda<Func<CourseInstance, TProperty(expression, parameter);
Usage
// his first suggestion - it works, retrieving the `Course` property of `CourseInstance`
var courses = courseInstancesQuery.Select(GetCourse())
// My modified overload above.
var courseNames = courseInstancesQuery.Select(GetCourseProperty<string>(c => c.Name));
Thoughts
The problem with the suggested implementation in my opinion is within the Expression.Call
line. Per MS docs:
Creates a MethodCallExpression that represents a call to a method that takes arguments.
However my desired expression contains no method calls - so I removed it and it worked. Now I simply use the delegate to extract the desired property's name and get that with another MemberAccessExpression
.
This is only my interpretation though. Happy to get corrected, if I am wrong.
Remarks: I recommend caching the typeof
calls in private fields, instead of calling them every time you build the expression. Also this can work for more then two derived classes (in my case InternalCourseInstance
and OpenCourseInstance
), you just need an extra ConditionalExpression
(s).
Edit
I've edited the code section - it seems Expression.Convert
is not supported by EntityFramework, however Expression.TypeAs
works just the same.
c#
c#
edited 15 hours ago
asked Nov 9 at 13:11
![](https://i.stack.imgur.com/TCSGv.jpg?s=32&g=1)
![](https://i.stack.imgur.com/TCSGv.jpg?s=32&g=1)
Alex
154115
154115
I´m not sure if this works for EF, but could you go fordynamic
turning your expression into something likeExpression<Func<dynamic, Course>>
? Not ideal, but as your design is broken anyway. In fact I hate myself for even suggesting it...
– HimBromBeere
Nov 9 at 13:15
@HimBromBeere I thought of that, however it does not solve the problem on conceptual level. I still don't have a way of getting theId
ofCourse
, if I need. And more often then not this is required inLINQ expressions and AutoMapper configurations, which means I cannot simply get theId
with follow upSelect
statement, for example. Please don't have yourself, I've expressed enough hate on this subject already :)
– Alex
Nov 9 at 14:21
add a comment |
I´m not sure if this works for EF, but could you go fordynamic
turning your expression into something likeExpression<Func<dynamic, Course>>
? Not ideal, but as your design is broken anyway. In fact I hate myself for even suggesting it...
– HimBromBeere
Nov 9 at 13:15
@HimBromBeere I thought of that, however it does not solve the problem on conceptual level. I still don't have a way of getting theId
ofCourse
, if I need. And more often then not this is required inLINQ expressions and AutoMapper configurations, which means I cannot simply get theId
with follow upSelect
statement, for example. Please don't have yourself, I've expressed enough hate on this subject already :)
– Alex
Nov 9 at 14:21
I´m not sure if this works for EF, but could you go for
dynamic
turning your expression into something like Expression<Func<dynamic, Course>>
? Not ideal, but as your design is broken anyway. In fact I hate myself for even suggesting it...– HimBromBeere
Nov 9 at 13:15
I´m not sure if this works for EF, but could you go for
dynamic
turning your expression into something like Expression<Func<dynamic, Course>>
? Not ideal, but as your design is broken anyway. In fact I hate myself for even suggesting it...– HimBromBeere
Nov 9 at 13:15
@HimBromBeere I thought of that, however it does not solve the problem on conceptual level. I still don't have a way of getting the
Id
of Course
, if I need. And more often then not this is required inLINQ expressions and AutoMapper configurations, which means I cannot simply get the Id
with follow up Select
statement, for example. Please don't have yourself, I've expressed enough hate on this subject already :)– Alex
Nov 9 at 14:21
@HimBromBeere I thought of that, however it does not solve the problem on conceptual level. I still don't have a way of getting the
Id
of Course
, if I need. And more often then not this is required inLINQ expressions and AutoMapper configurations, which means I cannot simply get the Id
with follow up Select
statement, for example. Please don't have yourself, I've expressed enough hate on this subject already :)– Alex
Nov 9 at 14:21
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
You have to create the expression using an expression-tree:
Expression<Func<CourseInstance, Course>> CreateExpression()
// (CourseInstance x) => x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
return Expression.Lambda<Func<CourseInstance, Course>>(expr, param);
ow you can use this expression by compiling it and call it:
var func = CreateExpression().Compile();
var courseInstance = new InternalCourseInstance Course = new Course Name = "MyCourse" ;
var result = func(courseInstance);
In order to get the CourseId
or the Name
from the instance you have to introduce a delegate that expects an instance of Course
and returns any arbitrary type T
. This means you´d need to add a call to that delegate in yoour expression-tree:
expr = Expression.Call(null, func.Method, expr);
The null
is important as your delegate that points to an anonymous method is translated to a static method from your compiler. If the delegate on the other hand points to a named non-static method, you should of course provide an instance for which this method is then called:
expr = Expression.Call(instanceExpression, func.Method, expr);
Be aware that your compiled method now returns a T
, not a Course
, so your final method looks like this:
Expression<Func<CourseInstance, T>> CreateExpression<T>(Func<Course, T> func)
// x => func(x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course)
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
expr = Expression.Call(null, func.Method, expr);
return Expression.Lambda<Func<CourseInstance, T>>(expr, param);
Thanks for the reply. If I understand correctly -result
will contain theCourse
object, regardless if I passCourseInstance
,InternalCourseInstance
orOpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.
– Alex
Nov 9 at 14:32
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,result
contains an instance ofCourse
.
– HimBromBeere
Nov 9 at 14:35
Still can't getExpression.Call
to work. It looks like this:Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception
– Alex
2 days ago
I am not sure what isstatic
and what is not. My whole Expression was defined as a static method, attached to my Data model -CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler -propertySelector.Method
is supposed to benull
, but I don't understand why.
– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
|
show 8 more comments
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
You have to create the expression using an expression-tree:
Expression<Func<CourseInstance, Course>> CreateExpression()
// (CourseInstance x) => x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
return Expression.Lambda<Func<CourseInstance, Course>>(expr, param);
ow you can use this expression by compiling it and call it:
var func = CreateExpression().Compile();
var courseInstance = new InternalCourseInstance Course = new Course Name = "MyCourse" ;
var result = func(courseInstance);
In order to get the CourseId
or the Name
from the instance you have to introduce a delegate that expects an instance of Course
and returns any arbitrary type T
. This means you´d need to add a call to that delegate in yoour expression-tree:
expr = Expression.Call(null, func.Method, expr);
The null
is important as your delegate that points to an anonymous method is translated to a static method from your compiler. If the delegate on the other hand points to a named non-static method, you should of course provide an instance for which this method is then called:
expr = Expression.Call(instanceExpression, func.Method, expr);
Be aware that your compiled method now returns a T
, not a Course
, so your final method looks like this:
Expression<Func<CourseInstance, T>> CreateExpression<T>(Func<Course, T> func)
// x => func(x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course)
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
expr = Expression.Call(null, func.Method, expr);
return Expression.Lambda<Func<CourseInstance, T>>(expr, param);
Thanks for the reply. If I understand correctly -result
will contain theCourse
object, regardless if I passCourseInstance
,InternalCourseInstance
orOpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.
– Alex
Nov 9 at 14:32
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,result
contains an instance ofCourse
.
– HimBromBeere
Nov 9 at 14:35
Still can't getExpression.Call
to work. It looks like this:Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception
– Alex
2 days ago
I am not sure what isstatic
and what is not. My whole Expression was defined as a static method, attached to my Data model -CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler -propertySelector.Method
is supposed to benull
, but I don't understand why.
– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
|
show 8 more comments
up vote
1
down vote
accepted
You have to create the expression using an expression-tree:
Expression<Func<CourseInstance, Course>> CreateExpression()
// (CourseInstance x) => x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
return Expression.Lambda<Func<CourseInstance, Course>>(expr, param);
ow you can use this expression by compiling it and call it:
var func = CreateExpression().Compile();
var courseInstance = new InternalCourseInstance Course = new Course Name = "MyCourse" ;
var result = func(courseInstance);
In order to get the CourseId
or the Name
from the instance you have to introduce a delegate that expects an instance of Course
and returns any arbitrary type T
. This means you´d need to add a call to that delegate in yoour expression-tree:
expr = Expression.Call(null, func.Method, expr);
The null
is important as your delegate that points to an anonymous method is translated to a static method from your compiler. If the delegate on the other hand points to a named non-static method, you should of course provide an instance for which this method is then called:
expr = Expression.Call(instanceExpression, func.Method, expr);
Be aware that your compiled method now returns a T
, not a Course
, so your final method looks like this:
Expression<Func<CourseInstance, T>> CreateExpression<T>(Func<Course, T> func)
// x => func(x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course)
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
expr = Expression.Call(null, func.Method, expr);
return Expression.Lambda<Func<CourseInstance, T>>(expr, param);
Thanks for the reply. If I understand correctly -result
will contain theCourse
object, regardless if I passCourseInstance
,InternalCourseInstance
orOpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.
– Alex
Nov 9 at 14:32
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,result
contains an instance ofCourse
.
– HimBromBeere
Nov 9 at 14:35
Still can't getExpression.Call
to work. It looks like this:Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception
– Alex
2 days ago
I am not sure what isstatic
and what is not. My whole Expression was defined as a static method, attached to my Data model -CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler -propertySelector.Method
is supposed to benull
, but I don't understand why.
– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
|
show 8 more comments
up vote
1
down vote
accepted
up vote
1
down vote
accepted
You have to create the expression using an expression-tree:
Expression<Func<CourseInstance, Course>> CreateExpression()
// (CourseInstance x) => x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
return Expression.Lambda<Func<CourseInstance, Course>>(expr, param);
ow you can use this expression by compiling it and call it:
var func = CreateExpression().Compile();
var courseInstance = new InternalCourseInstance Course = new Course Name = "MyCourse" ;
var result = func(courseInstance);
In order to get the CourseId
or the Name
from the instance you have to introduce a delegate that expects an instance of Course
and returns any arbitrary type T
. This means you´d need to add a call to that delegate in yoour expression-tree:
expr = Expression.Call(null, func.Method, expr);
The null
is important as your delegate that points to an anonymous method is translated to a static method from your compiler. If the delegate on the other hand points to a named non-static method, you should of course provide an instance for which this method is then called:
expr = Expression.Call(instanceExpression, func.Method, expr);
Be aware that your compiled method now returns a T
, not a Course
, so your final method looks like this:
Expression<Func<CourseInstance, T>> CreateExpression<T>(Func<Course, T> func)
// x => func(x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course)
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
expr = Expression.Call(null, func.Method, expr);
return Expression.Lambda<Func<CourseInstance, T>>(expr, param);
You have to create the expression using an expression-tree:
Expression<Func<CourseInstance, Course>> CreateExpression()
// (CourseInstance x) => x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
return Expression.Lambda<Func<CourseInstance, Course>>(expr, param);
ow you can use this expression by compiling it and call it:
var func = CreateExpression().Compile();
var courseInstance = new InternalCourseInstance Course = new Course Name = "MyCourse" ;
var result = func(courseInstance);
In order to get the CourseId
or the Name
from the instance you have to introduce a delegate that expects an instance of Course
and returns any arbitrary type T
. This means you´d need to add a call to that delegate in yoour expression-tree:
expr = Expression.Call(null, func.Method, expr);
The null
is important as your delegate that points to an anonymous method is translated to a static method from your compiler. If the delegate on the other hand points to a named non-static method, you should of course provide an instance for which this method is then called:
expr = Expression.Call(instanceExpression, func.Method, expr);
Be aware that your compiled method now returns a T
, not a Course
, so your final method looks like this:
Expression<Func<CourseInstance, T>> CreateExpression<T>(Func<Course, T> func)
// x => func(x is InternalCourseInstance ? ((InternalCourseInstance)x).Course : ((OpenCourseInstance).x).Course)
ParameterExpression param = Expression.Parameter(typeof(CourseInstance), "x");
Expression expr = Expression.TypeIs(param, typeof(InternalCourseInstance));
var cast1Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(InternalCourseInstance)), typeof(InternalCourseInstance).GetProperty(nameof(InternalCourseInstance.Course)));
var cast2Expr = Expression.MakeMemberAccess(Expression.Convert(param, typeof(OpenCourseInstance)), typeof(OpenCourseInstance).GetProperty(nameof(OpenCourseInstance.Course)));
expr = Expression.Condition(expr, cast1Expr, cast2Expr);
expr = Expression.Call(null, func.Method, expr);
return Expression.Lambda<Func<CourseInstance, T>>(expr, param);
edited yesterday
answered Nov 9 at 13:44
HimBromBeere
23k33158
23k33158
Thanks for the reply. If I understand correctly -result
will contain theCourse
object, regardless if I passCourseInstance
,InternalCourseInstance
orOpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.
– Alex
Nov 9 at 14:32
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,result
contains an instance ofCourse
.
– HimBromBeere
Nov 9 at 14:35
Still can't getExpression.Call
to work. It looks like this:Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception
– Alex
2 days ago
I am not sure what isstatic
and what is not. My whole Expression was defined as a static method, attached to my Data model -CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler -propertySelector.Method
is supposed to benull
, but I don't understand why.
– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
|
show 8 more comments
Thanks for the reply. If I understand correctly -result
will contain theCourse
object, regardless if I passCourseInstance
,InternalCourseInstance
orOpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.
– Alex
Nov 9 at 14:32
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,result
contains an instance ofCourse
.
– HimBromBeere
Nov 9 at 14:35
Still can't getExpression.Call
to work. It looks like this:Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception
– Alex
2 days ago
I am not sure what isstatic
and what is not. My whole Expression was defined as a static method, attached to my Data model -CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler -propertySelector.Method
is supposed to benull
, but I don't understand why.
– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
Thanks for the reply. If I understand correctly -
result
will contain the Course
object, regardless if I pass CourseInstance
, InternalCourseInstance
or OpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.– Alex
Nov 9 at 14:32
Thanks for the reply. If I understand correctly -
result
will contain the Course
object, regardless if I pass CourseInstance
, InternalCourseInstance
or OpenCourseInstance
? Would that work in LINQ to Entities? This goes a bit deeper then my experience. Will give it a go ASAP.– Alex
Nov 9 at 14:32
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,
result
contains an instance of Course
.– HimBromBeere
Nov 9 at 14:35
@Alex Me neither, however I assume EF is one of the reasons to even think about expression-trees at all. Otherwise you could just create the method as written in your question already. And yes,
result
contains an instance of Course
.– HimBromBeere
Nov 9 at 14:35
Still can't get
Expression.Call
to work. It looks like this: Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception– Alex
2 days ago
Still can't get
Expression.Call
to work. It looks like this: Expression.Call(null, propertySelector.Method, outerExpression)
and throws Static method requires null instance, non-static method requires non-null instance. Parameter name: method exception– Alex
2 days ago
I am not sure what is
static
and what is not. My whole Expression was defined as a static method, attached to my Data model - CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler - propertySelector.Method
is supposed to be null
, but I don't understand why.– Alex
2 days ago
I am not sure what is
static
and what is not. My whole Expression was defined as a static method, attached to my Data model - CourseInstance
, but this shouldn't be a problem and indeed it is not. According to the compiler - propertySelector.Method
is supposed to be null
, but I don't understand why.– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
Your non-generic suggestion also throws: Unable to cast the type 'CourseInstance' to type 'InternalCourseInstance'. LINQ to Entities only supports casting EDM primitive or enumeration types.
– Alex
2 days ago
|
show 8 more comments
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53226356%2fget-derived-class-property-from-parent-without-repetitive-casting-solved%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
I´m not sure if this works for EF, but could you go for
dynamic
turning your expression into something likeExpression<Func<dynamic, Course>>
? Not ideal, but as your design is broken anyway. In fact I hate myself for even suggesting it...– HimBromBeere
Nov 9 at 13:15
@HimBromBeere I thought of that, however it does not solve the problem on conceptual level. I still don't have a way of getting the
Id
ofCourse
, if I need. And more often then not this is required inLINQ expressions and AutoMapper configurations, which means I cannot simply get theId
with follow upSelect
statement, for example. Please don't have yourself, I've expressed enough hate on this subject already :)– Alex
Nov 9 at 14:21