在EntityFramework中使用SqlQuery来组成一个真正的IQueryable

时间:2015-08-01 14:56:42

标签: c# sql-server entity-framework

有没有办法让SqlQuery或任何其他sql执行方法组成一个模型对象,并允许像Include这样的方法工作?我这样做(其中view_products是一个SQL表函数):

Sub ReadUpdatedFiles()

Application.ScreenUpdating = False

Dim fso As New FileSystemObject
Dim fld As Folder
Dim f As File

Dim MyPath As String
MyPath = "\\filepath\folder"

Dim lastModified As Date
lastModified = FileDateTime(ThisWorkbook.path)

Dim sheetExists As Boolean
Dim ws As Worksheet
Dim sheetName As String
Dim mybook As Workbook
Dim sheetCounter As Integer

Set fld = fso.GetFolder(MyPath)
For Each f In fld.Files
    If f.Type = "CSV File" Then
        If f.DateLastModified - lastModified > 0 Then
            sheetExists = False
            sheetName = Left(f.Name, InStr(f.Name, ".") - 1)
            sheetCounter = 0
            For Each ws In Worksheets
                sheetCounter = sheetCounter + 1
                If ws.Name = sheetName Then
                    sheetExists = True
                End If
            Next ws

            Set mybook = Workbooks.Open(f.path)
            If sheetExists Then
                basebook.Sheets(sheetName).Delete
            Else
                sheetCounter = basebook.Sheets.Count
            End If
            mybook.Worksheets(1).Copy after:=ThisWorkbook.Sheets(sheetCounter)
        End If
    End If
Next f

Application.ScreenUpdating = True

End Sub

我想这样做:

var p = context.SqlQuery<Product>("select * from view_products(@param1)", new SqlParameter("param1", "somevalue"));

这让我得到了一个DbRawSqlQuery。我不能包含&#34;包括&#34;因为你不能在DbRawSqlQuery上这样做。所以,我最后添加一个AsQueryable,如:

var list = p.Include(i => i.Inventories).ToList();

但是,我知道这真的给了我一个IEnumerable,所以.Include不会产生任何结果。

我正在寻找另一种方法的建议,我从SQL函数中获取我的产品,然后加载相关的Inventory对象,以便我有一个对象图。谢谢!

1 个答案:

答案 0 :(得分:1)

SqlQuery提出了一些我尚未意识到的问题。

不包括

您正确解释了将Include转换为IEnumerableIQueryable没有任何效果的原因。 Include扩展了一个表达式,IEnumerable没有表达式(并且在将其转换为IQueryable之后不会神奇地得到一个表达式。)

没有附加

没有延迟加载

所以你最终得到的Product列表没有附加到上下文而不能延迟加载。您必须明确地将Product附加到上下文中(例如,将其状态设置为Unchanged)以使其延迟加载。

这是我刚刚发现的,而且很奇怪。实体被实现为代理对象,但与DbSet.AsNoTracking()提取的分离实体相反,它们不显示延迟加载。我认为这是一个错误,我报告了它。

但延迟加载会导致n + 1查询反模式:分别为每个产品获取Inventory对象的查询。因此,这不是获取对象图的最佳方式。

没有关系修复

所以你真正想要的是一次性加载属于这些产品的所有Inventory对象,并让 relationship fixup 完成它的工作(即EF自动填充所有Product.Inventories个集合):

context.Inventories.Where(i => productIds.Contains(i.ProductId)).Load();

...其中productIdsp中的产品ID列表。

但是,关系修正仅适用于附加实体,因此,您必须明确地将其状态更改为Unchanged。如果你这样做,你就准备好了...除非你没有。

同样,DbSet.AsNoTracking()提取的分离实体也存在差异。后者回应了关系修正,而SqlQuery的关系没有!

(相反)精心设计的解决方案

剩下的就是:加载上面显示的相关Inventory对象并自行填充Product.Inventories个收藏集:

var query = from product in p
            join inv context.Inventories.Local
                on product.ProductId equals inv.ProductId
                into inventories
            select new { product, inventories }
foreach(var anon in query)
{
    anon.product.Inventories = anon.inventories.ToList();
}

当然这完全不能令人满意。