简体   繁体   English

我可以使用什么强类型数据结构来保存具有不同形状的多个RecordSet的集合

[英]What Strongly-Typed Data-Structure can I use to hold a collection of multiple RecordSets with different shape

At the request of a responder to my original question here , I have been asked to reword the question in an effort to get to the bottom of the actual requirement. 应我这里原始问题的答复者的要求,我被要求对问题进行重新措词,以使实际需求降至最低。

What Strongly-Typed Data-Structure can I use to hold a collection of multiple RecordSets where each RecordSet will contain rows that may be of a different shape to the other RecordSets ? 我可以使用什么强类型数据结构来保存多个RecordSet的集合,其中每个RecordSet将包含形状可能与其他RecordSet不同的行?

This need is driven by the need to handle the data coming back from a Stored Procedure via a DbDataReader . 需要处理通过DbDataReader存储过程返回的数据。 The Stored Procedure may have multiple RecordSets , with each RecordSet returning different columns and number of columns. 存储过程可能具有多个RecordSet ,每个RecordSet返回不同的列和列数。

There will be DTO classes to represent each row of the respective data sets. 将有DTO类代表相应数据集的每一 These are known at compile time. 这些在编译时是已知的。 What I need is a data structure that can hold the multiple RecordSets , and for each RecordSet hold the DTOs representing rows returned from the RecordSet . 我需要的是一个可以容纳多个RecordSet的数据结构,并且对于每个RecordSet都包含代表从RecordSet返回的行的DTO

Example Stored procedure: 示例存储过程:

CREATE PROCEDURE [dbo].[MultipleRecordSetStoredProcedure]
    @Id                 INT
,   @Name               VARCHAR(20)
,   @Active             BIT
,   @Price              DECIMAL(10, 4)
,   @UniqueIdentifier   UNIQUEIDENTIFIER
,   @Count              TINYINT
AS
BEGIN
    /* First Record Set */
    SELECT 
        @Id AS Id
    ,   @Name AS Name
    UNION
    SELECT
        17 AS Id
    ,   'Bill' AS Name;

    /* Second Record Set */
    SELECT 
        @Name AS Name,
        @Active as Active
    UNION
    SELECT
        'Bill' AS Name
    ,   CAST(0 AS BIT) AS Active;

    /* Third Record Set */
    SELECT
        @UniqueIdentifier   AS UNIQUEIDENTIFIER
    ,   @Count              AS Count
    UNION
    SELECT
        NEWID() AS UNIQUEIDENTIFIER
   ,    CAST(10 AS TINYINT) AS Count;
END

Example calling code: 调用代码示例:

DbConnection connection = CreateConnection();
CommandBehavior commandBehavior = CommandBehavior.Default;

// Create a command to execute the stored storedProcedure
using (DbCommand command = connection.CreateStoredProcedureCommand(
    procedureName,
    procedureParameters,
    commandTimeout,
    transaction))
{
    // Populate a DataReder by calling the command
    using (DbDataReader reader = command.ExecuteReader(commandBehavior))
    {
        // Iterate through each result set...
        do
        {
            // Process the result set line by line
            while (reader.Read())
            {   
                // Read data into DataStructure
            }

        } while (reader.NextResult());
    }    
}

Example DTOs here: 此处的示例DTO:

internal class MultipleRecordSetStoredProcedureReturnType1
{
    public int Id { get; set; }
    public string Name { get; set; }
}

internal class MultipleRecordSetStoredProcedureReturnType2
{
    public bool Active { get; set; }
    public decimal Decimal { get; set; }
}

internal class MultipleRecordSetStoredProcedureReturnType3
{
    public Guid UniqueIdentifier { get; set; }
    public Byte Count { get; set; }
}

Ideally I do not want a list of objects or dynamics but a list of my DTOs for the recordset contents. 理想情况下,我不想要对象动态列表,而想要记录集内容的DTO列表。 Hopefully this will better clarify my original question. 希望这会更好地阐明我的原始问题。

In this case, I think it's better to keep things simple. 在这种情况下,我认为最好保持简单。

  1. You have a stored procedure that returns 3 result sets, so create a model that contains those 3 result sets. 您有一个存储过程返回3个结果集,因此请创建一个包含这3个结果集的模型。
  2. To fill your ResultModel , its better to use SqlDataAdapter and DataSet . 要填充ResultModel ,最好使用SqlDataAdapterDataSet It make things very simple, very very easier than data reader. 它使事情变得非常简单,比数据读取器非常容易。

The code for ResultModel: ResultModel的代码:

public class ResultModel
{
    public List<Type1> List1 { get; set; }
    public List<Type2> List2 { get; set; }
    public List<Type3> List3 { get; set; }
}

And I suppose these are your types: 我想这些是您的类型:

public class Type1
{
    public int A { get; set; }
}
public class Type2
{
    public int B { get; set; }
    public string C { get; set; }
}
public class Type3
{
    public int D { get; set; }
    public string E { get; set; }
    public string F { get; set; }
}

And you can fill your ResultModel, using SqlDataAdapter and DataSet: 您可以使用SqlDataAdapter和DataSet填充ResultModel:

public ResultModel GetData()
{
    var connection = @"data source=(localdb)\v11.0;initial catalog=TestDB;integrated security=True;MultipleActiveResultSets=True;";
    var command = "dbo.Procedure";
    var tableAdapter = new System.Data.SqlClient.SqlDataAdapter(command, connection);
    tableAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
    var dataSet = new DataSet();
    tableAdapter.Fill(dataSet); 

    var t1 = dataSet.Tables[0].Rows.Cast<DataRow>()
        .ToList().Select(row => new Type1
        {
            A = row.Field<int>("A"),
        }).ToList();

    var t2 = dataSet.Tables[1].Rows.Cast<DataRow>()
        .ToList().Select(row => new Type2
        {
            B = row.Field<int>("B"),
            C = row.Field<string>("C")
        }).ToList();

    var t3 = dataSet.Tables[1].Rows.Cast<DataRow>()
        .ToList().Select(row => new Type3
        {
            D = row.Field<int>("D"),
            E = row.Field<string>("E"),
            F = row.Field<string>("F")
        }).ToList();

    var result = new ResultModel() { List1 = t1, List2 = t2, List3 = t3 };

    return result;
}

The key points here are: 这里的关键点是:

  • We have a clean and simple ResultModel 我们有一个干净简单的ResultModel
  • Using SqlDataAdapter and a DataSet makes reading multiple result so easy. 使用SqlDataAdapterDataSet可以轻松读取多个结果。
  • Using Cast<DataRow>() enables us to use Linq against DataTable.Rows 使用Cast<DataRow>()使我们可以对DataTable.Rows使用Linq
  • Using Field<T>("field") enables us to get typed value of field 使用Field<T>("field")使我们能够获取字段的类型化值

Having read the original question, and this one, it sounds like you will ultimately end up with a List that contains within it other Lists each with their own type. 阅读了原始问题和这个问题之后,听起来您最终将得到一个列表,该列表中包含其他每个具有自己类型的列表。 As you iterate through the list you'll have no idea of what type to expect for the inner list, as it's variable. 当您遍历列表时,您将不知道内部列表的类型,因为它是可变的。 Given this you'll inevitably have to test for the type and then cast it, eg 鉴于此,您将不可避免地必须测试类型,然后进行转换,例如

foreach(var resultset in resultsets)
{
    if(resultset is MultipleRecordSetStoredProcedureReturnType1)
        //... cast it and do something...
}

Given that you'll inevitably have to test for the inner type and cast, even if you could achieve your goal I'm not sure it will serve any purpose in the end. 鉴于您将不可避免地必须测试内部类型和类型转换,即使您可以实现自己的目标,我也不确定它最终是否可以达到任何目的。 The list may as well just be 该列表可能也只是

List<List<Object>>

or perhaps 也许

List<List<IResultSet>>

As per the final example, you could consider defining an Interface for the inner type just to ensure that only objects that inherit from that interface are contained within it. 在最后一个示例中,您可以考虑为内部类型定义接口,以确保仅包含从该接口继承的对象。 This may be beneficial even if the interface doesn't define any behaviour and is just a simple declaration. 即使接口没有定义任何行为,而仅仅是一个简单的声明,这也可能是有益的。

I'm not sure it makes sense to return a List containing disparate results. 我不确定返回包含不同结果的List是否有意义。 I'd probably create a wrapper class to group your dto objects like below. 我可能会创建一个包装器类来将您的dto对象分组,如下所示。

public class RecordSetContainer
{
    public RecordSetContainer()
    {
        RecordSet1 = new List<MultipleRecordSetStoredProcedureReturnType1>();
        RecordSet2 = new List<MultipleRecordSetStoredProcedureReturnType2>();
        RecordSet3 = new List<MultipleRecordSetStoredProcedureReturnType3>();
    }
    public List<MultipleRecordSetStoredProcedureReturnType1> RecordSet1 { get; set; }
    public List<MultipleRecordSetStoredProcedureReturnType2> RecordSet2 { get; set; }
    public List<MultipleRecordSetStoredProcedureReturnType3> RecordSet3 { get; set; }
}

If you wanted to take the db code a step further to make it generic you could do something along these lines. 如果您想进一步提高数据库代码的通用性,则可以按照以下步骤进行操作。

public T CallMultipleRecordSetStoredProcedure<T>(
    params Expression<Func<T, IList>>[] recordSetPropertiesInOrder)
    where T : class, new()
{
    var outputType = typeof (T);
    var output = new T();

    // DbConnection & Command setup hidden 

    var recordSetNumber = 0;
    do
    {
        var outputRecordSetPropertyName =
            ((MemberExpression) recordSetPropertiesInOrder[recordSetNumber].Body).Member.Name;
        var dtoList = (IList) outputType.GetProperty(outputRecordSetPropertyName).GetValue(output);
        var dtoListType = dtoList.GetType().GetGenericArguments()[0];

        while (reader.Read())
        {
            var item = Activator.CreateInstance(dtoListType);
            var propertiesToWrite = dtoListType.GetProperties().Where(p => p.CanWrite);

            foreach (var property in propertiesToWrite)
            {
                property.SetValue(
                    item,
                    Convert.ChangeType(reader[property.Name], property.PropertyType));
            }
            dtoList.Add(item);
        }

        recordSetNumber++;
    } while (reader.NextResult());

    return output;
}

Not the prettiest or readable code I admit but it should allow you to call any multi record set stored procedure as long as the property names match the column names. 我承认的不是最漂亮或可读的代码,但只要属性名称与列名称匹配,它就应该允许您调用任何多记录集存储过程。 One downside with this code is that while the compiler will enforce you to only select IList properties in you expressions it is unable to ensure you pick a generic list (which the method itself actually requires). 此代码的缺点是,尽管编译器将强制您仅在表达式中选择IList属性,但无法确保您选择通用列表(该方法本身实际需要)。

For your example case the method would be called as below 对于您的示例情况,该方法将如下调用

CallMultipleRecordSetStoredProcedure<RecordSetContainer>(
    rsc => rsc.RecordSet1,
    rsc => rsc.RecordSet2,
    rsc => rsc.RecordSet3);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我可以使用什么数据结构来表示.Net中的强类型2D数据矩阵? - What data structures can I use to represent a strongly-typed 2D matrix of data in .Net? 如何将聚合物库与强类型视图一起使用? - How can i use Polymer library with Strongly-Typed View? 如何在服务器标签中使用强类型资源? - How can I use strongly-typed resources in server tags? Objective-C转换为C#:如果字典是强类型的,如何在C#中使用多个类型的.plist? - Objective-C converting to C#: How can I use a .plist with multiple types in C# if Dictionary is strongly-typed? 如何在启动 class 中使用强类型配置 - How can I use a strongly-typed config within the Startup class 如何使用强型卫星组件? - How to use strongly-typed satellite assembly? 将强类型的数据传递给ViewMasterPage和ViewPage - Passing Strongly-Typed Data to a ViewMasterPage and ViewPage 绑定到ASP.NET MVC中的强类型对象的集合 - Binding to a Collection of Strongly-Typed Objects in ASP.NET MVC 如何使用Linq从XML获取强类型集合 - How to get strongly-typed collection from XML using Linq 如何在运行时将现有的强类型数据集连接到其他服务器? - How to connect an existing strongly-typed data set to a different server at run time?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM