65.9K
CodeProject 正在变化。 阅读更多。
Home

LINQ 和 Visual Studio for Web 2010 中的多个数据集

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.47/5 (5投票s)

2013年10月3日

CPOL

5分钟阅读

viewsIcon

12509

解释了如何在一次调用存储过程时检索多个数据集。

引言

在 ASP.NET 中使用 LINQ 从数据库返回多个数据集。

我过去曾使用过 LINQ,但有一个要求是从单个存储过程调用返回多个“数据集”。

背景

当我们使用 Visual Studio 创建 .dbml 文件时,我们可以浏览可用的数据库。然后,我们可以将表从数据库拖到工作区域,Visual Studio 将按需创建所有必要的类作为类型。同样,如果我们拖动一个存储过程到工作区域,Visual Studio 将检查该过程并从结果集中创建一个类。这个类将用作调用存储过程将返回的对象的“类型”。

这种方法的一个问题是,当您将存储过程拖到工作区域时,Visual Studio 将创建一个匹配单个返回类型的类。过去,开发人员会从存储过程返回的数据创建一个数据集,并将其映射到数据集中的单独的数据表。开发人员可以根据需要重命名这些表,并且可以通过调用datasetname.firstdatatable.columnnamedatasetname.seconddatatable.columnname来单独访问其中的数据。

使用 Visual Studio 实现这一点的方法并不直接。您只会得到一个dataset,因为默认情况下,Visual Studio 创建的类会创建一个返回类型为ISingleResult的函数,例如:

public ISingleResult<s_GetFDProductsToReviewResult>
s_GetFDProductsToReview(
   [global::System.Data.Linq.Mapping.ParameterAttribute(Name="Enabled",
DbType="Bit")] System.Nullable<bool> enabled,
[global::System.Data.Linq.Mapping.ParameterAttribute(Name="StockCode",
DbType="VarChar(30)")] string StockCode,
[global::System.Data.Linq.Mapping.ParameterAttribute(Name="AgeRange",
DbType="VarChar(2)")] string ageRange,
[global::System.Data.Linq.Mapping.ParameterAttribute(Name="Gender",
DbType="Char(1)")] System.Nullable<char> gender,
[global::System.Data.Linq.Mapping.ParameterAttribute(Name="Reviewed",
DbType="Bit")] System.Nullable<bool> reviewed)
{
IExecuteResult result = this.ExecuteMethodCall(this,
 ((MethodInfo)(MethodInfo.GetCurrentMethod())), enabled, StockCode, ageRange, gender, reviewed);
return ((ISingleResult<s_GetFDProductsToReviewResult>)(result.ReturnValue));
}

好的,我承认这看起来很复杂,但实际上并非如此。

该函数是公共的,其返回类型为ISingleResult,类型为s_GetFDProductsToReviewResult

请记住,s_GetFDProductsToReviewResult是 Visual Studio 从存储过程返回的数据创建的类(在这种情况下,它也是实际存储过程的名称!)。

此函数接受多个参数。

第一个参数是

[global::System.Data.Linq.Mapping.ParameterAttribute(Name="Enabled", DbType="Bit")] System.Nullable<bool> enabled

第二个参数是

[global::System.Data.Linq.Mapping.ParameterAttribute(Name="StockCode", DbType="VarChar(30)")] string StockCode

第三个参数是

global::System.Data.Linq.Mapping.ParameterAttribute(Name="AgeRange", DbType="VarChar(2)")] string ageRange

等等。

在函数体内部,调用一个内部方法 (ExecuteMethodCall),并将前面描述的参数传递给它。

该函数返回已执行存储过程的数据,将其强制转换为类型

return ((IsingleResult<s_GetFDProductsToReviewResult>)(result.ReturnValue));

Bingo!我们得到了数据。

唯一的问题是,此函数返回它找到的第一个数据集。

要捕获存储过程返回的所有数据集,我们必须创建一个返回类型为IMultipleResult的方法。我们还必须创建将映射到存储过程返回的数据集的对象。现在,我的存储过程返回两个数据集,第一个是数据库表中我们感兴趣的所有记录的计数。第二个数据集将只包含我们感兴趣的数据库表中所有记录中的 20 条记录。这是为了向用户显示“显示 20 条记录,共 40 条”。随着我更新数据库表中的记录,我想能够显示“显示 20 条记录,共 39 条”,依此类推,直到剩余记录少于 20 条。

那么如何同时获取这两个数据集呢?

做到这一点的方法是创建 2 个映射到返回的数据集的类。

我可以通过将一个部分类添加到我的项目中并费力地创建所有必需的属性等来手动完成此操作。

最简单的方法是转到存储过程,注释掉将成为第一个数据集的第一个 SELECT 语句。

完成后,我可以删除 **dbml** 文件中的存储过程,然后保存 DBML 文件,之后将存储过程拖到我的 dbml 文件工作区域。

如果您转到 _designer.cs_ 文件,您将看到 Visual Studio 创建的部分类。在我的例子中,这是记录计数。将该方法复制到安全的地方,因为稍后您将需要它。然后删除 DBML 文件中的存储过程并保存 DBML 文件。

接下来,回到存储过程,取消注释第一个 SELECT 语句,然后注释掉 **第二个 SELECT 语句**。

重复上述步骤。保存存储过程,转到 Visual Studio 并**刷新数据库资源管理器**。

转到 DBML 文件并将存储过程拖到工作区域。检查 _designer.cs_ 文件,您将看到一个不同的类,它映射到存储过程中第二个 SELECT 语句创建的第二个数据集。

现在我们需要将第一个类的代码复制回来,我们将其保存在安全的地方。

_designer.cs_ 文件现在看起来会像这样

一些私有信息,包括类的构造函数和一些连接字符串……后面是

//[global::System.Data.Linq.Mapping.FunctionAttribute(Name="Tesco.s_GetFDProductsToReview")]
//public ISingleResult<s_GetFDProductsToReviewResult> s_GetFDProductsToReview(
 //[global::System.Data.Linq.Mapping.ParameterAttribute(Name="Enabled", 
 //        DbType="Bit")] //System.Nullable<bool> enabled,
//[global::System.Data.Linq.Mapping.ParameterAttribute(Name="SysproStockCode",
   //DbType="VarChar(30)")] string sysproStockCode,
 //[global::System.Data.Linq.Mapping.ParameterAttribute(Name="AgeRange",
 //   DbType="VarChar(2)")] //string ageRange, 
//[global::System.Data.Linq.Mapping.ParameterAttribute(Name="Gender", 
//       DbType="Char(1)")] //System.Nullable<char> gender, 
 //[global::System.Data.Linq.Mapping.ParameterAttribute(
 //     Name="Reviewed", DbType="Bit")] //System.Nullable<bool> reviewed)
//{
//    IExecuteResult result = this.ExecuteMethodCall(this, 
//        ((MethodInfo)(MethodInfo.GetCurrentMethod())), //enabled, sysproStockCode, ageRange, gender, reviewed);
//    return ((ISingleResult<s_GetFDProductsToReviewResult>)(result.ReturnValue));
//}

上面的代码在 Visual Studio 中被注释掉了,因为它是我第一次调用数据库时创建的默认函数,我需要替换它。

这是第一个类,即它映射到只包含记录计数的第一个数据集。

public partial class s_GetFDProductsToReviewResult__RecordsCount
{

private System.Nullable<int> _RecordsCount;

public s_GetFDProductsToReviewResult__RecordsCount()
{
}

[global::System.Data.Linq.Mapping.ColumnAttribute(
  Storage="_RecordsCount", DbType="Int")]
public System.Nullable<int> RecordsCount
{
    get
    {
        return this._RecordsCount;
    }
    set
    {
        if ((this._RecordsCount != value))
        {
            this._RecordsCount = value;
        }
    }
}
}

这是映射到数据库中用户需要查看的实际行的第二个类。

public partial class s_GetFDProductsToReviewResult
    {
        private string _PSE_SysProStockCode;
        private string _Name;
        private string _ImageUrl;
        private System.Nullable<bool> _PSE_Enabled;
        private int _PSE_DisableReason;
        private System.Nullable<System.DateTime> _PSE_EditedDate;
        private string _PSE_EditedBy;
        private System.Nullable<bool> _Reviewed;
        
        public s_GetFDProductsToReviewResult()
        {
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_PSE_SysProStockCode", 
               DbType = "NVarChar(30) NOT NULL", CanBeNull = false)]
        public string PSE_SysProStockCode
        {
            get
            {
                return this._PSE_SysProStockCode;
            }
            set
            {
                if ((this._PSE_SysProStockCode != value))
                {
                    this._PSE_SysProStockCode = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_Name", DbType = "VarChar(100)")]
        public string Name
        {
            get
            {
                return this._Name;
            }
            set
            {
                if ((this._Name != value))
                {
                    this._Name = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_ImageUrl", DbType = "NVarChar(258)")]
        public string ImageUrl
        {
            get
            {
                return this._ImageUrl;
            }
            set
            {
                if ((this._ImageUrl != value))
                {
                    this._ImageUrl = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_PSE_Enabled", DbType = "Bit")]
        public System.Nullable<bool> PSE_Enabled
        {
            get
            {
                return this._PSE_Enabled;
            }
            set
            {
                if ((this._PSE_Enabled != value))
                {
                    this._PSE_Enabled = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_PSE_DisableReason", DbType = "Int NOT NULL")]
        public int PSE_DisableReason
        {
            get
            {
                return this._PSE_DisableReason;
            }
            set
            {
                if ((this._PSE_DisableReason != value))
                {
                    this._PSE_DisableReason = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_PSE_EditedDate", DbType = "DateTime")]
        public System.Nullable<System.DateTime> PSE_EditedDate
        {
            get
            {
                return this._PSE_EditedDate;
            }
            set
            {
                if ((this._PSE_EditedDate != value))
                {
                    this._PSE_EditedDate = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_PSE_EditedBy", DbType = "VarChar(50)")]
        public string PSE_EditedBy
        {
            get
            {
                return this._PSE_EditedBy;
            }
            set
            {
                if ((this._PSE_EditedBy != value))
                {
                    this._PSE_EditedBy = value;
                }
            }
        }

        [global::System.Data.Linq.Mapping.ColumnAttribute(
          Storage = "_Reviewed", DbType = "Bit")]
        public System.Nullable<bool> Reviewed
        {
            get
            {
                return this._Reviewed;
            }
            set
            {
                if ((this._Reviewed != value))
                {
                    this._Reviewed = value;
                }
            }
        }
    }
}

避免了大量的打字工作!

现在我们需要做的是修改实际调用存储过程的函数,使其返回IMultipleResults类型而不是ISingleResult类型。

如果您回顾上面的代码,您可以看到当我们将存储过程拖到 DBML 文件工作区域时,Visual Studio 默认会生成什么。我们需要修改此代码,以便它返回两个我们可以获取的数据集。

要做到这一点,我们需要给方法添加一些装饰,并将代码修改为以下内容

//This maps the name of the stored procedure. 
global::System.Data.Linq.Mapping.FunctionAttribute(Name="myDB.s_GetFDProductsToReview")]  
//The code below shows the attributes we need to decorate the method with: 
[ResultType(typeof(s_GetFDProductsToReviewResult__RecordsCount))] 
[ResultType(typeof(s_GetFDProductsToReviewResult))]   

//The code below is the function declaration including all the parameters it needs 
//notice it will return IMultipleResults ...
public IMultipleResults GetMultipleResultSet(
[global::System.Data.Linq.Mapping.ParameterAttribute(Name =
"Enabled", DbType = "Bit")] System.Nullable<bool>
enabled, 
[global::System.Data.Linq.Mapping.ParameterAttribute(Name =
"SysproStockCode", DbType = "VarChar(30)")]
string sysproStockCode,
[global::System.Data.Linq.Mapping.ParameterAttribute(Name =
"AgeRange", DbType = "VarChar(2)")] string ageRange, 
[global::System.Data.Linq.Mapping.ParameterAttribute(Name =
"Gender", DbType = "Char(1)")]System.Nullable<char> gender,

[global::System.Data.Linq.Mapping.ParameterAttribute(Name =
"Reviewed", DbType = "Bit")]System.Nullable<bool> reviewed) 
{
    try{
    IExecuteResult result = this.ExecuteMethodCall(this,
((MethodInfo)(MethodInfo.GetCurrentMethod())), enabled, sysproStockCode, ageRange, gender, reviewed);
    return ((IMultipleResults)(result.ReturnValue));
    }
 catch (Exception ex){throw new Exception(ex.Message);
}
}

就是这样,现在我们只需要在我们的主应用程序中调用 _designer.cs_ 页面中找到的方法。

类似

MyDataContext tdc = new MyDataContext();
protected void BindData(){ 

reviewed = getReviewed();gender = getGender();
enabled = getShow();
agerange = getAgeRange();

var products = tdc.GetMultipleResultSet(enabled,sysstockcode, agerange, gender,reviewed);
            
IList<s_GetFDProductsToReviewResult__RecordsCount>cnt =
products.GetResult<s_GetFDProductsToReviewResult__RecordsCount>().ToList();

List<s_GetFDProductsToReviewResult> records =
products.GetResult<s_GetFDProductsToReviewResult>().ToList();

lbl_RecordCount.Text= "Viewing "+records.Count+" of " + cnt[0].RecordsCount +"records" ;  
GridView1.DataSource = records;
GridView1.DataBind();

本文最初是一篇快速的“操作指南”,供我自己使用。我希望它能为其他人节省一些时间和/或痛苦。

© . All rights reserved.