簡體   English   中英

我可以使用什么強類型數據結構來保存具有不同形狀的多個RecordSet的集合

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

應我這里原始問題的答復者的要求,我被要求對問題進行重新措詞,以使實際需求降至最低。

我可以使用什么強類型數據結構來保存多個RecordSet的集合,其中每個RecordSet將包含形狀可能與其他RecordSet不同的行?

需要處理通過DbDataReader存儲過程返回的數據。 存儲過程可能具有多個RecordSet ,每個RecordSet返回不同的列和列數。

將有DTO類代表相應數據集的每一 這些在編譯時是已知的。 我需要的是一個可以容納多個RecordSet的數據結構,並且對於每個RecordSet都包含代表從RecordSet返回的行的DTO

示例存儲過程:

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

調用代碼示例:

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());
    }    
}

此處的示例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; }
}

理想情況下,我不想要對象動態列表,而想要記錄集內容的DTO列表。 希望這會更好地闡明我的原始問題。

在這種情況下,我認為最好保持簡單。

  1. 您有一個存儲過程返回3個結果集,因此請創建一個包含這3個結果集的模型。
  2. 要填充ResultModel ,最好使用SqlDataAdapterDataSet 它使事情變得非常簡單,比數據讀取器非常容易。

ResultModel的代碼:

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

我想這些是您的類型:

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; }
}

您可以使用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;
}

這里的關鍵點是:

  • 我們有一個干凈簡單的ResultModel
  • 使用SqlDataAdapterDataSet可以輕松讀取多個結果。
  • 使用Cast<DataRow>()使我們可以對DataTable.Rows使用Linq
  • 使用Field<T>("field")使我們能夠獲取字段的類型化值

閱讀了原始問題和這個問題之后,聽起來您最終將得到一個列表,該列表中包含其他每個具有自己類型的列表。 當您遍歷列表時,您將不知道內部列表的類型,因為它是可變的。 鑒於此,您將不可避免地必須測試類型,然后進行轉換,例如

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

鑒於您將不可避免地必須測試內部類型和類型轉換,即使您可以實現自己的目標,我也不確定它最終是否可以達到任何目的。 該列表可能也只是

List<List<Object>>

也許

List<List<IResultSet>>

在最后一個示例中,您可以考慮為內部類型定義接口,以確保僅包含從該接口繼承的對象。 即使接口沒有定義任何行為,而僅僅是一個簡單的聲明,這也可能是有益的。

我不確定返回包含不同結果的List是否有意義。 我可能會創建一個包裝器類來將您的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; }
}

如果您想進一步提高數據庫代碼的通用性,則可以按照以下步驟進行操作。

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;
}

我承認的不是最漂亮或可讀的代碼,但只要屬性名稱與列名稱匹配,它就應該允許您調用任何多記錄集存儲過程。 此代碼的缺點是,盡管編譯器將強制您僅在表達式中選擇IList屬性,但無法確保您選擇通用列表(該方法本身實際需要)。

對於您的示例情況,該方法將如下調用

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM