Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从父级获取派生类属性,无需重复转换_C# - Fatal编程技术网

C# 从父级获取派生类属性,无需重复转换

C# 从父级获取派生类属性,无需重复转换,c#,C#,我正试图解决数据模型结构中的设计失败引起的麻烦。重构不是一个选项,因为EF变得疯狂。ASP.NET4.6框架 结构如下: class Course { // properties defining a Course object. Example: Marketing course public string Name { get; set; } } class CourseInstance { // properties that define an Instan

我正试图解决数据模型结构中的设计失败引起的麻烦。重构不是一个选项,因为EF变得疯狂。ASP.NET4.6框架

结构如下:

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; }
}
我打赌你已经看到了这个缺陷了?事实上,出于未知的原因,有人决定将
CourseId
及其导航属性放在派生类型上,而不是放在父类型上。现在,每次我想从
课程安装
访问
课程
,我都会执行以下操作:

x.course => courseInstance is InternalCourseInstance
    ? (courseInstance as InternalCourseInstance).Course
    : (courseInstance as OpenCourseInstance).Course;
通过从
CourseInstance
派生的多个课程实例类型,您可以看到这是如何变得非常丑陋的

我正在寻找一种简写的方法,本质上是创建一个方法或表达式,它在内部执行。然而,还有一个问题-它必须可翻译为SQL,因为在
IQueryable
上更经常地使用这种转换

我最接近的解决方案是:

// CourseInstance.cs
public static Expression<Func<CourseInstance, Course>> GetCourseExpression =>
    t => t is OpenCourseInstance
        ? (t as OpenCourseInstance).Course
        : (t as InternalCrouseInstance).Course
用法
//他的第一个建议——它有效,检索'CourseInstance'的'Course'属性`
var courses=courseInstancesQuery.Select(GetCourse())
//上面是我修改过的重载。
var courseNames=courseInstancesQuery.Select(GetCourseProperty(c=>c.Name));
思想 在我看来,建议的实现的问题在
表达式.Call
行中。Per:

创建MethodCallExpression,该表达式表示对接受参数的方法的调用

然而,我想要的表达式不包含任何方法调用,所以我删除了它,它就工作了。现在,我只需使用委托提取所需属性的名称,并使用另一个
MemberAccessExpression
获取该名称

不过,这只是我的解释。如果我错了,很高兴得到纠正

备注:我建议在私有字段中缓存
typeof
调用,而不是每次构建表达式时都调用它们。同样,这可以用于两个以上的派生类(在我的例子中是
InternalCourseInstance
OpenCourseInstance
),您只需要一个额外的
ConditionalPression

编辑
我编辑了代码部分-EntityFramework似乎不支持
Expression.Convert
,但是
Expression.TypeAs
的工作原理是一样的。

您必须使用表达式树创建表达式:

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);
}
expr = Expression.Call(null, func.Method, expr);
为了从实例中获取
CourseId
名称
,您必须引入一个委托,该委托需要
Course
的实例并返回任意类型的
T
。这意味着您需要在表达式树中添加对该委托的调用:

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);
}
expr = Expression.Call(null, func.Method, expr);
null
非常重要,因为指向匿名方法的委托将从编译器转换为静态方法。另一方面,如果委托指向一个命名的非静态方法,您当然应该提供一个实例,然后为该实例调用此方法:

expr = Expression.Call(instanceExpression, func.Method, expr);
请注意,编译后的方法现在返回的是
T
,而不是
课程
,因此最终的方法如下所示:

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);
}
Expression创建表达式(Func Func)
{
//x=>func(x是InternalCourseInstance?((InternalCourseInstance)x)。课程:((OpenCourseInstance.x)。课程)
ParameterExpression param=表达式参数(typeof(CourseInstance),“x”);
expr=Expression.TypeIs(参数,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);
返回表达式.Lambda(expr,param);
}

我不确定这是否适用于EF,但您是否可以使用
动态
将您的表达式转换为类似
表达式
?不太理想,但你的设计已经被打破了。事实上,我恨我自己甚至建议它…@HimBromBeere我想到了这一点,但它并不能解决概念层面的问题。如果需要,我仍然没有办法获得
课程的
Id
。通常情况下,Inq表达式和AutoMapper配置中不需要这样做,这意味着我不能简单地使用follow-up
Select
语句获取
Id
。请不要有你自己,我已经对这个问题表达了足够的仇恨:)请将你的解决方案转移到它自己的答案上来,谢谢。谢谢你的回复。如果我理解正确-
结果
将包含
课程
对象,无论我通过
课程实例
内部课程实例
还是
开放课程实例
?这在LINQ中对实体有效吗?这比我的经验更深刻。我也不会,但是我认为EF是考虑表达式树的原因之一。否则,您可以按照问题中所写的方式创建方法。是的,
result
包含一个
Course
的实例。仍然无法获取
表达式。请调用
以工作。它看起来是这样的:
Expression.Call(null,propertySelector.Method,outerExpression)
并抛出静态方法需要null实例,非静态方法需要非null实例。参数名称:方法例外我不确定什么是静态的,什么不是。我的整个表达式被定义为一个静态方法,附加到我的数据模型-
CourseInstance
,但这不应该是一个问题,事实上也不是。根据编译器-
propertySelector.Method
是sup
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);
}