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.










share|improve this question























  • 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















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.










share|improve this question























  • 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













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.










share|improve this question















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#






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 15 hours ago

























asked Nov 9 at 13:11









Alex

154115




154115











  • 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

















  • 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
















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













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);






share|improve this answer






















  • 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











  • 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










  • 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 Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















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






























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);






share|improve this answer






















  • 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











  • 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










  • 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















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);






share|improve this answer






















  • 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











  • 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










  • 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













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);






share|improve this answer














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);







share|improve this answer














share|improve this answer



share|improve this answer








edited yesterday

























answered Nov 9 at 13:44









HimBromBeere

23k33158




23k33158











  • 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











  • 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










  • 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










  • @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











  • 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
















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


















 

draft saved


draft discarded















































 


draft saved


draft discarded














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














































































Popular posts from this blog

Darth Vader #20

How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

Ondo