c# - IQueryable 和模型绑定(bind)魔术?

标签 c# asp.net

我正在测试用于 Web 窗体的新 ASP.NET 4.5 模型绑定(bind),使用一个简单的存储库公开 IQueryable。该存储库使用 EF 5,数据库优先方法。我正在投影 EF 自动生成的实体以使用我的 DTO。

一切正常,这就是重点,我期待看到某种异常...

这是代码:

存储库

    public IQueryable<JobDto> GetJobs()
    {
        var ctx = this.contextResolver.GetCurrentContext<pubsEntities>();

        return ctx.jobs.Select(x => new JobDto
            {
                Description = x.job_desc,
                ID = x.job_id,
                Maximum = x.max_lvl,
                Minimum = x.min_lvl
            });
    }

如您所见,我将我的 EF 实体投影到自定义 DTO 中,并且属性完全不同。

ASPX代码隐藏

    public IQueryable<JobDto> gv_GetData()
    {
        return this.jobsRepository.GetJobs();
    }

ASPX

    <asp:GridView runat="server" ID="gv" AllowPaging="true" AllowSorting="true"
        DataKeyNames="ID"
        AutoGenerateColumns="true"
        SelectMethod="gv_GetData"
        ItemType="QueryRepository.JobDto, QueryRepository">
        <Columns>
            <asp:BoundField DataField="Description" HeaderText="My custom description" SortExpression="Description" />
        </Columns>
    </asp:GridView>

这就像一个魅力,分页和排序开箱即用当使用一个很棒的存储库时(现在我不必使用 ObjectDataSource 来简化分页和排序等事情)

我的问题是:

如果我的存储库返回 IQueryable<JobDto>并且我的 DTO 的属性与我的 EF 实体(这是一个名为:job 的不同实体)的属性同名。

EF 怎么可能对我的 GridView 进行排序?正确自从我的GridView配置了我的 DTO 实体中定义的属性名称???据我所知,使用 LINQ 的动态排序是使用字符串来设置顺序标准来完成的。 LINQ to Entities - IQueryable正在将我的 DTO 属性自动映射到我的 EF 实体公开的属性。

任何人都可以帮助这个可怜的灵魂 =( 了解幕后发生的事情吗??

我运行一个 SQL 配置文件只是为了确认查询在数据库中正确执行:

SELECT TOP (10) 
[Project1].[C1] AS [C1], 
[Project1].[job_desc] AS [job_desc], 
[Project1].[job_id] AS [job_id], 
[Project1].[max_lvl] AS [max_lvl], 
[Project1].[min_lvl] AS [min_lvl]
FROM ( SELECT [Project1].[job_id] AS [job_id], [Project1].[job_desc] AS [job_desc], [Project1].[min_lvl] AS [min_lvl], [Project1].[max_lvl] AS [max_lvl], [Project1].[C1] AS [C1], row_number() OVER (ORDER BY [Project1].[job_desc] DESC) AS [row_number]
    FROM ( SELECT 
        [Extent1].[job_id] AS [job_id], 
        [Extent1].[job_desc] AS [job_desc], 
        [Extent1].[min_lvl] AS [min_lvl], 
        [Extent1].[max_lvl] AS [max_lvl], 
        1 AS [C1]
        FROM [dbo].[jobs] AS [Extent1]
    )  AS [Project1]
)  AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[job_desc] DESC

请特别注意这些行 (ASPX):

<asp:BoundField DataField="Description" SortExpression="Description" />

以及生成的 SQL

ORDER BY [Project1].[job_desc] DESC

最佳答案

使用这段代码:

return ctx.jobs.Select(x => new JobDto
    {
        Description = x.job_desc,
        ID = x.job_id,
        Maximum = x.max_lvl,
        Minimum = x.min_lvl
    });

Select您正在调用的重载实际上是在创建一个新的 IQueryable这会影响 EF 发出的实际 SQL。您还没有真正将数据对象投影到您的 DTO,而是给 EF 一个表达式,它可以用来为结果集创建查询,一旦查询运行,该结果集稍后将投影到您的 DTO。请注意,在 Select被调用的重载,一个 Expression<Func<Job, JobDto>>正在通过,而不仅仅是 Func<Job, JobDto> .因为 EF 分析表达式,所以它能够在可能的情况下对 SQL 进行复杂的转换。

OrderBy表达式由您的 GridView 添加, 它也只是在修改你的 IQueryable使用可以转换为 SQL 的新表达式。

编辑:

当您调用 Select 时在 IQueryable<Job> jobs 上您的上下文属性,EF 能够查看您的 Expression<Job, JobDto>并确定:

  1. 您的 JobDto 需要哪些列投影
  2. 如何JobDto投影由 Job 填充表。

如果你看一下很多Expression BCL 中的类(class),你会看到它是如何做到这一点的。编译器,遇到x => new JobDto { Description = x.job_desc, ... }时, 创建一个看起来像这样的复杂表达式树(我正在严格简化它):

LambdaExpression<Func<Job, JobDto>>
    MemberInitExpression
        NewExpression
        Bindings
            MemberAssignment
                Member = Description property
                Expression = MemberExpression representing access to the Job property
            MemberAssignment...
            MemberAssignment...
            MemberAssignment...
            etc.

您可以看到此树如何包含足够的信息供 EF 遍历表达式并生成内部映射并生成等效的 SQL 命令。他们基本上是将 .NET 表达式投影到 SQL 表达式。并非所有内容都具有 1:1 映射,但在您的情况下,您可以看到映射是多么简单:

Job type          -> Extent1 alias   -> dbo.jobs table
JobDto projection -> Project1 alias  -> subquery

您会注意到还有其他投影;它引入了一个行号属性和一些包含值 1 的神秘属性;我不确定它的用途。

OrderBy然后是一个额外的扩充,其中 Expression进行了分析。

关于c# - IQueryable 和模型绑定(bind)魔术?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12792166/

相关文章:

c# - 以编程方式调整 DataGridView 的大小以删除滚动条

c# - .NET CLR 线程池耗尽 - 实现错误?

c# - knockout View 模型+所选项目->嵌套列表 "not defined"

c# - PostAsJsonAsync 返回 null

c# - 如何更改 ASP.Net MVC 5 中的默认 "[field value] already taken"错误消息?

c# - asp :GridView 中 ItemTemplate 的点击事件

c# - 使用部分类和设计器文件将 Visual Studio 2003 表单转换为 Visual Studio 2005/2008 表单

c# - 启用“代码优化”选项时,应用程序崩溃

c# - asp.net中的后台进程

c# - CSS 模板不适用于 Login.aspx 页面