ASP.NET MVC和LINQ常见问题

16

我之前在这个主题上问过一些问题。在我们能够在工作中实现MVC或LINQ之前,我们需要解决一些问题。

ASP.NET MVC中的多个记录集

目前使用MVC的所有示例仅返回单个结果集。当使用存储过程时,可以检索到多个记录集,并且我们通常使用存储过程的两个原因是(我相信你们许多人也知道)。首先,以防我们需要传递参数,其次,如果我们想要返回多个数据表。在ASP.NET的MVC架构中如何实现呢?

此教程中,我们看到了如何检索数据。但它使用了ViewData.Model,这表示一个单一的结果集,它没有解释如何获得多个结果集或如何获取它们。

强类型存储过程输出

此外,在ASP.NET网站上使用LINQ进行强类型输出分辨率的示例是通过使用*.dbml格式实现的,该格式是对表模式的镜像,允许使用LINQ针对字段进行查找。很好。但是,如果您的输出来自不直接映射到视图或表的存储过程,该怎么办?我们如何解决这些存储过程的列名?

在前一节中,我描述了此教程,但它也仅显示如何为表创建LINQ to SQL,而不是来自sproc的自定义输出。

LINQ列查找

在工作中,我们运行一个宏,将一堆类导出到我们的App_Code文件夹中,以便预定义存储过程参数。这样做是为了避免调用DeriveParameters,因为这会多次调用数据库。由于现有的流量已经很大,我们不希望发生这种情况。如果我们使用LINQ,如何解析列数据类型?每当我们定义参数时,是否需要调用数据库以查找参数的数据类型和名称?事情是否改变了?它仍然每次调用DeriveParameters吗?这些是否缓存在某个地方?

DBML格式

*.dbml文件是否应包含数据库中的所有表?我们有大约15个数据库,每个数据库中都有许多表。

为每个输出创建一个视图

在这篇文章中再加上另一点。即使是自定义输出,代替手动创建dbml类,是否更好地将数据表示为视图?或者在dbml文件中创建自定义类更好?

这一定是最后的问题,否则我会吃掉我的胳膊

"无法将类型为'SingleResult`1[IntranetMVC.UserDetail]'的对象强制转换为类型'IntranetMVC.UserDetail'。"

以下是函数内容:

  Function Index() As ActionResult
    ViewData("Message") = "Welcome to ASP.NET MVC!"

    Dim userDetail As UserDetail
    Dim office As IList(Of Office)
    Dim activeUser As IList(Of ActiveUser)
    Dim dept As IList(Of Department)

    Using db As PersonnelDataContext = New PersonnelDataContext
      Dim results As IMultipleResults = db.UserDetail(1168)

      userDetail = results.GetResult(Of UserDetail)()
      office = results.GetResult(Of Office)()
      activeUser = results.GetResult(Of ActiveUser)()
      dept = results.GetResult(Of Department)()
    End Using

    Return View(New IndexViewData(userDetail, office, activeUser, dept))
  End Function

此问题出现在所有userDetail, office, activeUserdept分配上,但我不知道原因。尽管我还没有正确映射它们,以部门(Department)为例,我已经将表架构拖放到 dbml 文件中,因此它肯定存在且格式正确。

更新

这是我的实际代码。 它不是最终版,我一直在尝试改进它。似乎返回类型不正确,但我不确定原因。它似乎认为仅返回单个结果,而存储过程实际上返回四个数据集。其中一个数据集始终只有一个结果,其他数据集总是返回多行:

无法将类型为'SingleResult1 [IntranetMVC.Office]'的对象强制转换为类型'System.Collections.Generic.IList1'

Imports System.Data.Linq
Imports System.Reflection
Imports System.Data.Linq.Mapping

Partial Class PersonnelDataContext

  <FunctionAttribute(Name:="dbo.UserDetailProc"), _
  ResultType(GetType(UserDetail)), _
  ResultType(GetType(IList(Of Office))), _
  ResultType(GetType(IList(Of ActiveUser))), _
  ResultType(GetType(IList(Of Department)))> _
  Public Function UserDetail( _
                  <Parameter(Name:="User_Key", DbType:="Int")> ByVal User_Key As Integer, _
                  <Parameter(Name:="EditYN", DbType:="Char")> Optional ByVal EditYN As Char = "N") As IMultipleResults

    Dim result As IExecuteResult = Me.ExecuteMethodCall(Me, CType(MethodInfo.GetCurrentMethod(), MethodInfo), User_Key, EditYN)
    Return CType(result.ReturnValue, IMultipleResults)
  End Function
End Class

FIX

由于我没有正确检查返回类型,所以我并没有意识到这一点。我认为从IMultipleResults获取结果的results.GetResult(Of MyType)会返回一个集合,但实际上它只返回单个结果,并将指针移动到集合中的下一项。不幸的是,GetResult是唯一暴露的方法来返回结果,因此您必须迭代集合并将其添加到泛型列表中。

非常感谢!

2个回答

21

ASP.NET MVC中的多记录集

是的 - 绝对没错。

首先,您需要手动创建一个调用存储过程并返回IMultipleResults结果的方法。

这篇博客文章包含了您所需的所有信息。这很容易做到,并且非常简单易行。

您需要完成两个步骤:

  1. 创建一个调用存储过程并返回多条记录的方法(请参阅上面的博客文章)。
  2. 创建一个简单的类对象,该对象在视图中使用,而控制器设置属性。

例如:

IndexViewData.cs
public class IndexViewData
{
    IList<Customers> Customers { get; set; }
    IList<Products> Products { get; set; }
}

.

HomeController.cs
public ActionResult Index()
{
    IList<Customers> customers;
    IList<Products> products;

    // This grabs the multiple records from a single stored procedure. 
    // This code taken from the blog post link, above.
    using (NorthwindDataContext db = new NorthwindDatacontext)
    {
        IMultipleResults results = db.GetMultipleRecordSets(arg1, ....);
        customers = results.GetResult<Customer>();
        products = results.GetProducts<Product>();
    }

    // Now return the view, with the viewdata that is required.
    return View(new IndexViewData
                    {
                        Customers = customers,
                        Products = products
                    });
}

.

Index.aspx
<%@ Page 
    Language="C#" 
    MasterPageFile="~/Views/Shared/Site.Master" 
    Inherits="System.Web.Mvc.ViewPage<IndexViewData>" %>

<% Html.RenderPartial("CustomersUserControl", 
                       ViewData.Model.Customers); %>

 <br/>

<h2>Products</h2>
<% foreach(var product in ViewData.Model.Products) { %>
Name: <%= product.Name %><br/>
<% } %>

...

请注意,我没有进行任何错误检查等操作。这是一个非常快速的伪代码指南,让您开始入门。
请注意,Index视图是强类型的(它继承了ViewPage)。
强类型存储过程输出
我已经在上面回答了这个问题。请注意,您可以为ISingleResult存储过程设置强类型。
LINQ列查找
好的,我想我明白你的意思了。 当您创建调用存储过程的方法时(无论是ISingleResult还是IMultipleResult),您正在定义所需的参数...将其视为硬编码。
当您将表拖放到linq to sql上下文gui画布上时,Visual Studio会进行查找检查。然后在上下文的各个文件之一中创建类。例如NorthwindDataContext.designer等。因此,这是一次性工作。创建类后,设计师将在画布上显示此类。没有将数据同步回数据库。没有。如果您更改数据库模式中的任何内容(例如添加新字段,更改存储过程参数等),则数据上下文将不知道它。您需要删除表格并将其重新拖放回来。
额外技巧!
如果您在拖放表或存储过程到画布上时运行SQL Profiler,则可以看到Visual Studio正在查询信息。:)

嗯,这是一种"发射并忘记"的方式。只需要一次操作,但需要手动同步。

希望有所帮助。

更新

我注意到你添加了两个问题,所以我会在这里添加我的答案。

DBML格式

这是一个个人决定。15个数据库!天啊!这是相当多的数量。无论如何,它取决于您的上下文画布变得有多可维护。 其次,每个上下文都创建自己的数据库连接。所以如果您的方法决定调用4个上下文,那么您就有了4个连接(和往返)到数据库,伙计 :)

为每个输出创建视图

就我个人而言,我将所有表都放在上下文画布上。我从不在我的代码中使用那些表类。它们是私有的,仅在我的存储库命名空间/项目/dll中使用。然后,我使用POCO类来移动所有内容。这使得我的代码更加干净,不依赖于存储库。

更新#2

这一定是最后的问题,否则我会吃掉我的手臂

如果您已经将存储过程拖到了linq上下文画布上,请将其删除。不需要引用方法UserDetails(int userId)

现在,请将以下代码添加到数据上下文的部分类中(顺便说一句,我假设您知道它是什么意思):

[Function("UserDetails")] // <-- This is the name of your stored procedure.
[ResultType(TypeOf(UserDetail))]
[ResultType(TypeOf(Office))]
[ResultType(TypeOf(ActiveUser))]
[ResultType(TypeOf(Department))]
public IMultipleResults UserDetails(
    [Parameter(Name = "UserId", DbType = "Int")] int userId)
//                      /\____     /\_____         ____/\                    
// This is where u _define_ the stored proc arguments.
{
    IExecuteResult result = this.ExecuteMethodCall(this, 
           ((MethodInfo)MethodInfo.GetCurrentMethod())), userId);
// This is where all the stored proc arguments are set ____/\
// This can be multiple args. eg. userId, name, ...
    return (IMultipleResults)result.ReturnValue;
}

然后像你以前的VB.NET代码一样使用它。

问题(我猜)是您还没有编写处理 IMultipleResults 的方法。您仍在使用旧的存储过程代码签名,该签名默认情况下仅为单个记录集结果(即 ISingleResult )。

如果您从Server Explorer拖放存储过程到Linq Context Canvas上,则这是默认设置。


1
啊,伙计 - 我明白了。我更新了最后一段,请重新阅读并告诉我是否理解正确(即我的问题三的理解)。 - Pure.Krome
1
添加了更多的答案,因为您增加了2个问题。 - Pure.Krome
1
  1. Context Canvas是在查看dbml文件时所看到的内容。正确。
  2. 我将XXXX_ViewData类与它们所代表的视图并排放置。因此,如果这是Home控制器的Index视图,则我会将IndexViewData放在View\Home文件夹中。其他人会将其放在Models文件夹中,并给它一个独特的名称。
- Pure.Krome
1
尝试创建一个新的SO帖子,其中包含C#代码,并请求有人将其转换为VB。呃.. VB,可怜的家伙。 - Pure.Krome
1
很好的东西Kezzer-很高兴听到它在运行。当你把它搞定的时候,我真的很喜欢这个L2S东西!@ Marc:多谢啦,老兄!从你嘴里说出来对我来说意义非凡 :) 再次感谢。Mucho respecto :) - Pure.Krome
显示剩余12条评论

3

ASP.NET MVC 中的多个记录集:

如果您有返回 A 和 B 的存储过程。那么您需要创建一个特定的 ViewModel:

public class AB
{
  public A DataA { get; set; };
  public B DataB { get; set; };
}

您也可以使用ViewData字典而非Model属性(或与该属性结合使用)

强类型存储过程输出

您可以创建一个特定的类,用于从存储过程返回具有自定义字段的结果。

LINQ列查找

不确定这一点是否完全正确,但是LINQ在设计时从存储过程查找列字段名称和参数名称。


第一点:很好。不过对于第一点,我并不是完全确定。你能举个小例子详细说明一下吗?第二点:那么如何创建这个类?只需要在models文件夹中吗?它应该长什么样子?第三点:那么查找是每次访问时执行,还是只执行一次? - Kieran Senior
1
嗨,迈克尔。当我在打我的回复时,我看到你发了你的(但是在我发完之前没有读它)。我们遵循了相同的格式和类似的回复 :) 不要进入我的脑海!:) :) - Pure.Krome
嗨Kezzer,正如你所看到的,Pure.Krome的回答比我更好。但如果你想的话,我仍然可以回答。 :-) - Michael

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