实体框架,表格类型和Linq - 获取“类型”

3
我有一个名为Product的抽象类型,它包括五个继承自Product的“类型”,以表格形式按类型层次结构组织如下:
我想获取所有产品的所有信息,包括从继承自产品的不同对象中选取的一些属性,将它们投影到一个新类中,以在MVC网页中使用。我的linq查询如下:
     //Return the required products
    var model = from p in Product.Products
                where p.archive == false && ((Prod_ID == 0) || (p.ID == Prod_ID))
                select new SearchViewModel 
                    {
                        ID = p.ID,
                        lend_name = p.Lender.lend_name,
                        pDes_rate = p.pDes_rate,
                        pDes_details = p.pDes_details,
                        pDes_totTerm = p.pDes_totTerm,
                        pDes_APR = p.pDes_APR,
                        pDes_revDesc = p.pDes_revDesc,
                        pMax_desc = p.pMax_desc,
                        dDipNeeded = p.dDipNeeded,
                        dAppNeeded = p.dAppNeeded,      

                        CalcFields = new DAL.SearchCalcFields
                        {
                            pDes_type = p.pDes_type,
                            pDes_rate = p.pDes_rate,
                            pTFi_fixedRate = p.pTFi_fixedRate 
                        }
                    }

我的问题是访问p.pTFi_fixedRate,它不随实体的产品集合返回,因为它在Fixed超类型中。如何使用Linq和Entity Framework返回产品的“超”类型(Fixed)属性?我需要返回所有不同超类型(Disc、Track等)的一些字段,以便进行计算。我应该将它们作为单独的Linq查询返回,并检查返回的“Product”的类型吗?

如果将来有人参考此内容,下面的 Union 将会起作用。Linq to Entities 会写出肮脏的 SQL,而且不仅如此,如果你返回复杂的查询,Linq 将需要很长时间来遍历表达式树以实际构建 SQL。在我的情况下,SQL 运行了 1 秒钟(500 行),而 Linq 花了 3 秒钟来组合它。我解决这个问题的方法是放弃 TPT,改为使用视图进行返回。 - MagicalArmchair
3个回答

1

所以Fixed是从Product继承而来的?如果是这样,你应该查询Fixed而不是Product,Product属性将被拉入其中。

如果你只是做一些计算并得到一些总数或其他东西,你可能想考虑使用存储过程。这将减少数据库调用次数并允许更快的执行。


感谢您的回复@CodeWarrior。正确的,修正的和其他4种“超级”类型都是从Product继承而来的。从业务逻辑的角度来看,这些计算非常复杂,因此我一直试图将代码保留在C#方法中,而不是SQL缩放器函数或SPs中。我能否在5个不同的Linq查询中查询每个类型,然后将它们全部粘合在一起返回数据,在实现时进行计算?还是那只是令人讨厌的?这将返回一个IQueryable,因此我正在尝试将执行推迟到最后一刻(例如当MVC网站上发生分页时)。 - MagicalArmchair
在我有一堆HPT类的情况下,如果我绝对必须这样做,我通常会将它们全部查询到一个List<Object>集合中。然后,我可以通过遍历并评估TypeOf来执行任何操作。 - CodeWarrior
谢谢CodeWarrior,但我想避免将它们实例化为ToList<>,因为它是一个贪婪的运算符。我不禁认为你尽可能接近数据的选项是正确的,在SPs或DB函数中sigh。我将首先尝试@Crab Buckets的解决方案,创建一个使用扩展方法的Union。我假设Union是一种惰性方法,所以它可能会奏效... - MagicalArmchair
通常对于那些可能(将)最终进行许多数据库调用或获取大量数据以执行其他小操作(算术计数),我会创建存储过程。网络流量更少,执行周期更少等等。请告诉我发生了什么,因为我也可以预见到这将成为我的问题。 - CodeWarrior

1

这是一个非常好的问题。我查看了Julie Lerman的书籍并在互联网上搜索,但没有找到优雅的答案。

如果是我,我会创建一个数据传输对象,其中包含所有类型的属性,然后为每种类型单独查询,最后将它们全部合并。我会在DTO属性中插入空白,以表示该类型不相关的属性。然后我希望EF引擎能够合理地创建出不错的SQL语句。

例如:

var results = (from p in context.Products.OfType<Disc>
        select new ProductDTO {basefield1 = p.val1, discField=p.val2, fixedField=""})
         .Union(
        from p in context.Products.OfType<Fixed>
        select new ProductDTO {basefield1 = p.val1, discField="", fixedField=p.val2});

但这不能是最好的答案,对吧。还有其他的吗?


你好,Crab Bucket,再次感谢你的建议:)你可以看到我正在努力使用Entity Framework来发挥其最大的作用。我没有考虑使用Union扩展方法,我会尝试一下并查看性能如何。如果性能不佳,我将尝试通过SP或标量值函数计算这些数据,就像@CodeWarriors建议的那样。还要感谢你提供的示例,它们真的很有帮助。 - MagicalArmchair
没有问题 - 我认为我们中很多人都在努力解决这个问题。说实话 - 我刚刚又读了一遍Julie Lerman的书中有关TPT继承部分,虽然TPT看起来不错,但我怀疑它使用起来是否容易?你的问题是一个很好的例子,还存在在基类已经有记录时插入记录到继承类型中的问题。看起来并不容易。似乎很容易被锁定在继承角落里。当然,这只是我的个人意见。祝你好运 - Crab Bucket
EDM中的实际设置非常容易,从理想的角度来看,它确实很有道理,但在实践中,我发现自己渴望ADO.net、Tableadapters和Datasets的控制。这可能是因为我还没有习惯EF中的工作方式。我尝试了联合方法,它确实有效,我需要将三个Linq查询链接在一起,并使用Unions。当执行时,从5个不同的表中从数据库中检索出35个字段的20行数据,实体框架编写了超过370行的SQL代码!!更重要的是,在第一次运行时,这需要3秒钟才能执行。 - MagicalArmchair
在调整了我的Linq之后,SQL语句减少到了250行,听起来仍然相当绝望,但是进一步的调查发现它只需要30毫秒就能执行完,所以也许我需要更多地相信这个框架,停止过分关注它所写的SQL(考虑到SQL团队其实是将实体框架组合在一起的人)。如果性能成为问题,我可能仍然会回到@CodeWarriors的解决方案,但是,就目前而言,这实际上工作得很好。感谢您的参与! - MagicalArmchair

0

这取决于你的模型,但通常你需要做类似这样的事情:

var model = from p in Product.Products.Include("SomeNavProperty")
.... (rest of query)

其中SomeNavProperty是加载pTFi_fixedRate的实体类型。


感谢@kprobst的输入!这只适用于实体框架中存在关系而不是继承的情况,对吗?因此,要包括来自Lender的数据,我可以这样做,但是从Product继承的对象没有导航属性以Product为类型。 - MagicalArmchair
啊,我现在明白你的意思了。没事 :) - kprobst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接