programing

DataReader를 목록으로 쉽게 변환하려면 어떻게 해야 합니까?

megabox 2023. 4. 19. 22:53
반응형

DataReader를 목록으로 쉽게 변환하려면 어떻게 해야 합니까?

는 i에 데이터가 DataReader을 가가 a a a로 하고 싶다.List<T>이를 위한 가능한 간단한 해결책은 무엇입니까?

CustomerEntity CustomerId CustomerName namenameity 。가 이 두 DataReader 2로 ?List<CustomerEntity>.

이를 위해 확장 방법을 작성할 것을 권장합니다.

public static IEnumerable<T> Select<T>(this IDataReader reader,
                                       Func<IDataReader, T> projection)
{
    while (reader.Read())
    {
        yield return projection(reader);
    }
}

LINQ를 할 수 .ToList()을 「」으로하는 방법.List<T>한면면면면면면면면 면

using (IDataReader reader = ...)
{
    List<Customer> customers = reader.Select(r => new Customer {
        CustomerId = r["id"] is DBNull ? null : r["id"].ToString(),
        CustomerName = r["name"] is DBNull ? null : r["name"].ToString() 
    }).ToList();
}

는 사실 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★FromDataReader in the method in the method in 。Customer(어느 쪽인가?

public static Customer FromDataReader(IDataReader reader) { ... }

그러면 다음과 같이 됩니다.

using (IDataReader reader = ...)
{
    List<Customer> customers = reader.Select<Customer>(Customer.FromDataReader)
                                     .ToList();
}

(이 경우 유형 추론이 효과가 없을 것 같지만 내가 틀릴 수 있습니다.)

저는 이 케이스를 사용하여 다음과 같은 방법을 썼습니다.

네임스페이스를 합니다.System.Reflection

::T type및 return type(ClassName)입니다.dr는 맵핑 입니다.DataReader

C#, 다음과 같은 콜 매핑 방식:

List<Person> personList = new List<Person>();
personList = DataReaderMapToList<Person>(dataReaderForPerson);

매핑 방법은 다음과 같습니다.

public static List<T> DataReaderMapToList<T>(IDataReader dr)
{
    List<T> list = new List<T>();
    T obj = default(T);
    while (dr.Read()) {
        obj = Activator.CreateInstance<T>();
        foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
            if (!object.Equals(dr[prop.Name], DBNull.Value)) {
                prop.SetValue(obj, dr[prop.Name], null);
            }
        }
        list.Add(obj);
    }
    return list;
}

VB.NET, 다음과 같은 콜 매핑 방식:

Dim personList As New List(Of Person)
personList = DataReaderMapToList(Of Person)(dataReaderForPerson)

매핑 방법은 다음과 같습니다.

Public Shared Function DataReaderMapToList(Of T)(ByVal dr As IDataReader) As List(Of T)
        Dim list As New List(Of T)
        Dim obj As T
        While dr.Read()
            obj = Activator.CreateInstance(Of T)()
            For Each prop As PropertyInfo In obj.GetType().GetProperties()
                If Not Object.Equals(dr(prop.Name), DBNull.Value) Then
                    prop.SetValue(obj, dr(prop.Name), Nothing)
                End If
            Next
            list.Add(obj)
        End While
        Return list
    End Function

속성이나 필드의 Reflection과 속성을 사용하여 DataReader를 객체에 매핑하는 시스템을 본 적이 있습니다(LinqToSql과 약간 비슷합니다).DBNull 등을 코딩할 때 약간의 입력이 저장되며 오류 수를 줄일 수 있습니다.생성된 코드를 캐시하면 대부분의 손으로 작성된 코드보다 더 빠를 수 있습니다. 따라서 이 작업을 많이 하는 경우에는 "하이로드"를 고려하십시오.

"반사의 방어"를 참조하십시오.「NET참조해 주세요.

그런 다음 다음과 같은 코드를 쓸 수 있습니다.

class CustomerDTO  
{
    [Field("id")]
    public int? CustomerId;

    [Field("name")]
    public string CustomerName;
}

...

using (DataReader reader = ...)
{    
   List<CustomerDTO> customers = reader.AutoMap<CustomerDTO>()
                                    .ToList();
}

(AutoMap()은 확장 방식입니다.)


@Stilgar씨, 좋은 코멘트 감사합니다.

NHibernate, EF 또는 Linq를 SQL에 더 잘 사용할 수 있는 경우 오래된 프로젝트(또는 "여기서 발명되지 않았다", "저장된 프로를 좋아한다" 등)에서는 항상 ORM을 사용할 수 있는 것은 아니기 때문에 경량 시스템이 "소매"에 도움이 될 수 있습니다.

IDataReader 루프를 많이 쓸 필요가 있는 경우, 작업하고 있는 시스템의 아키텍처를 변경하지 않아도 코딩(및 에러)을 줄일 수 있는 이점이 있습니다.그렇다고 해서 처음부터 이 아키텍처가 좋은 것은 아닙니다.

고객님은DTO는 데이터 액세스 레이어에서 나오지 않으며 복합 객체 등은 DTO 객체를 사용하여 데이터 액세스 레이어에 의해 구축됩니다.


내가 이 답을 쓴 지 몇 년 후 Dapper는 의 세계로 들어갔다.NET은 온웨어 AutoMapper를 쓰기 위한 매우 좋은 출발점이 될 수 있습니다.아마도 그렇게 할 필요가 완전히 없어질 것입니다.

가장 심플한 솔루션:

var dt = new DataTable();
dt.Load(myDataReader);
List<DataRow> rows = dt.AsEnumerable();
var customers = rows.Select(dr=>new Customer(...)).ToList();

는 대퍼를 사용하기 시작했다(그리고 그랬을 것이다.예를 들면, (메모리에서 기입된) 다음과 같습니다.

public List<CustomerEntity> GetCustomerList()
{
    using (DbConnection connection = CreateConnection())
    {
        return connection.Query<CustomerEntity>("procToReturnCustomers", commandType: CommandType.StoredProcedure).ToList();
    }
}

CreateConnection()는 사용자의 DB에 액세스하여 연결을 반환하는 작업을 처리합니다.

Dapper는 데이터 필드를 속성에 자동으로 매핑합니다.또한 여러 유형과 결과 세트를 지원하며 매우 빠릅니다.

쿼리 반환IEnumerable따라서ToList().

물론. 뻔하지.@Ian Ringrose이를 위해 라이브러리를 사용해야 한다는 님의 중심 논제는 여기서 가장 적합한 단일 답변입니다(+1 참조).단, 최소한의 일회용 코드 또는 데모 코드를 위해 여기 구체적인 예가 있습니다.@SLaks에 대한 은근한 코멘트@Jon Skeet님의 자세한 답변(+1'd):

public List<XXX> Load( <<args>> )
{
    using ( var connection = CreateConnection() )
    using ( var command = Create<<ListXXX>>Command( <<args>>, connection ) )
    {
        connection.Open();
        using ( var reader = command.ExecuteReader() )
            return reader.Cast<IDataRecord>()
                .Select( x => new XXX( x.GetString( 0 ), x.GetString( 1 ) ) )
                .ToList();
    }
}

에서와 같이@Jon Skeet의 답변은

            .Select( x => new XXX( x.GetString( 0 ), x.GetString( 1 ) ) )

비트를 헬퍼로 추출할 수 있습니다(쿼리 클래스로 덤프합니다).

    public static XXX FromDataRecord( this IDataRecord record)
    {
        return new XXX( record.GetString( 0 ), record.GetString( 1 ) );
    }

및 다음 용도로 사용됩니다.

            .Select( FromDataRecord )

업데이트 3월 9일 13일: 이 답변에서 보일러 플레이트를 분할하기 위한가지 뛰어난 추가 미묘한 코딩 기법도 참조하십시오.

단순히 (직접) 데이터레이더를 목록으로 변환할 수 없습니다.

데이터레이더의 모든 요소를 루프하여 목록에 삽입해야 합니다.

샘플 코드 이하

using (drOutput)   
{
            System.Collections.Generic.List<CustomerEntity > arrObjects = new System.Collections.Generic.List<CustomerEntity >();        
            int customerId = drOutput.GetOrdinal("customerId ");
            int CustomerName = drOutput.GetOrdinal("CustomerName ");

        while (drOutput.Read())        
        {
            CustomerEntity obj=new CustomerEntity ();
            obj.customerId = (drOutput[customerId ] != Convert.DBNull) ? drOutput[customerId ].ToString() : null;
            obj.CustomerName = (drOutput[CustomerName ] != Convert.DBNull) ? drOutput[CustomerName ].ToString() : null;
            arrObjects .Add(obj);
        }

}

이걸 애완동물 프로젝트에서 다룬 적이 있는데..원하는 것을 사용하세요.

ListEx는 IDataReader 인터페이스를 구현합니다.


people = new ListExCommand(command)
.Map(p=> new ContactPerson()
{
  Age = p.GetInt32(p.GetOrdinal("Age")),
  FirstName = p.GetString(p.GetOrdinal("FirstName")),
  IdNumber = p.GetInt64(p.GetOrdinal("IdNumber")),
  Surname = p.GetString(p.GetOrdinal("Surname")),
  Email = "z.evans@caprisoft.co.za"
})
.ToListEx()
.Where("FirstName", "Peter");

또는 다음 예시와 같이 객체 매핑을 사용합니다.


people = new ListExAutoMap(personList)
.Map(p => new ContactPerson()
{
    Age = p.Age,
    FirstName = p.FirstName,
    IdNumber = p.IdNumber,
    Surname = p.Surname,
    Email = "z.evans@caprisoft.co.za"
})
.ToListEx()
.Where(contactPerson => contactPerson.FirstName == "Zack");

http://caprisoft.codeplex.com 를 참조해 주세요.

이 질문이 오래되고 이미 답한 건 알지만...

SqlDataReader는 이미 IEnumerable을 구현하고 있기 때문에 레코드에 루프를 작성할 필요가 있는 이유는 무엇입니까?

지금까지 IList, List(Of T), IENumerable(Of T), IENumerable(Of T), IQueryable(Of T) 및 IQueryable(Of T)을 사용하여 테스트했습니다.

Imports System.Data.SqlClient
Imports System.Data
Imports System.Threading.Tasks

Public Class DataAccess
Implements IDisposable

#Region "   Properties  "

''' <summary>
''' Set the Query Type
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property QueryType() As CmdType
    Set(ByVal value As CmdType)
        _QT = value
    End Set
End Property
Private _QT As CmdType

''' <summary>
''' Set the query to run
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property Query() As String
    Set(ByVal value As String)
        _Qry = value
    End Set
End Property
Private _Qry As String

''' <summary>
''' Set the parameter names
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property ParameterNames() As Object
    Set(ByVal value As Object)
        _PNs = value
    End Set
End Property
Private _PNs As Object

''' <summary>
''' Set the parameter values
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property ParameterValues() As Object
    Set(ByVal value As Object)
        _PVs = value
    End Set
End Property
Private _PVs As Object

''' <summary>
''' Set the parameter data type
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property ParameterDataTypes() As DataType()
    Set(ByVal value As DataType())
        _DTs = value
    End Set
End Property
Private _DTs As DataType()

''' <summary>
''' Check if there are parameters, before setting them
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Private ReadOnly Property AreParams() As Boolean
    Get
        If (IsArray(_PVs) And IsArray(_PNs)) Then
            If (_PVs.GetUpperBound(0) = _PNs.GetUpperBound(0)) Then
                Return True
            Else
                Return False
            End If
        Else
            Return False
        End If
    End Get
End Property

''' <summary>
''' Set our dynamic connection string
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Private ReadOnly Property _ConnString() As String
    Get
        If System.Diagnostics.Debugger.IsAttached OrElse My.Settings.AttachToBeta OrElse Not (Common.CheckPaid) Then
            Return My.Settings.DevConnString
        Else
            Return My.Settings.TurboKitsv2ConnectionString
        End If
    End Get
End Property

Private _Rdr As SqlDataReader
Private _Conn As SqlConnection
Private _Cmd As SqlCommand

#End Region

#Region "   Methods "

''' <summary>
''' Fire us up!
''' </summary>
''' <remarks></remarks>
Public Sub New()
    Parallel.Invoke(Sub()
                        _Conn = New SqlConnection(_ConnString)
                    End Sub,
                    Sub()
                        _Cmd = New SqlCommand
                    End Sub)
End Sub

''' <summary>
''' Get our results
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetResults() As SqlDataReader
    Try
        Parallel.Invoke(Sub()
                            If AreParams Then
                                PrepareParams(_Cmd)
                            End If
                            _Cmd.Connection = _Conn
                            _Cmd.CommandType = _QT
                            _Cmd.CommandText = _Qry
                            _Cmd.Connection.Open()
                            _Rdr = _Cmd.ExecuteReader(CommandBehavior.CloseConnection)
                        End Sub)
        If _Rdr.HasRows Then
            Return _Rdr
        Else
            Return Nothing
        End If
    Catch sEx As SqlException
        Return Nothing
    Catch ex As Exception
        Return Nothing
    End Try
End Function

''' <summary>
''' Prepare our parameters
''' </summary>
''' <param name="objCmd"></param>
''' <remarks></remarks>
Private Sub PrepareParams(ByVal objCmd As Object)
    Try
        Dim _DataSize As Long
        Dim _PCt As Integer = _PVs.GetUpperBound(0)

        For i As Long = 0 To _PCt
            If IsArray(_DTs) Then
                Select Case _DTs(i)
                    Case 0, 33, 6, 9, 13, 19
                        _DataSize = 8
                    Case 1, 3, 7, 10, 12, 21, 22, 23, 25
                        _DataSize = Len(_PVs(i))
                    Case 2, 20
                        _DataSize = 1
                    Case 5
                        _DataSize = 17
                    Case 8, 17, 15
                        _DataSize = 4
                    Case 14
                        _DataSize = 16
                    Case 31
                        _DataSize = 3
                    Case 32
                        _DataSize = 5
                    Case 16
                        _DataSize = 2
                    Case 15
                End Select
                objCmd.Parameters.Add(_PNs(i), _DTs(i), _DataSize).Value = _PVs(i)
            Else
                objCmd.Parameters.AddWithValue(_PNs(i), _PVs(i))
            End If
        Next
    Catch ex As Exception
    End Try
End Sub

#End Region

#Region "IDisposable Support"

Private disposedValue As Boolean ' To detect redundant calls

' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    If Not Me.disposedValue Then
        If disposing Then
        End If
        Try
            Erase _PNs : Erase _PVs : Erase _DTs
            _Qry = String.Empty
            _Rdr.Close()
            _Rdr.Dispose()
            _Cmd.Parameters.Clear()
            _Cmd.Connection.Close()
            _Conn.Close()
            _Cmd.Dispose()
            _Conn.Dispose()
        Catch ex As Exception

        End Try
    End If
    Me.disposedValue = True
End Sub

' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
Protected Overrides Sub Finalize()
    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    Dispose(False)
    MyBase.Finalize()
End Sub

' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    Dispose(True)
    GC.SuppressFinalize(Me)
End Sub

#End Region

End Class

강력한 타이핑 클래스

Public Class OrderDCTyping
    Public Property OrderID As Long = 0
    Public Property OrderTrackingNumber As String = String.Empty
    Public Property OrderShipped As Boolean = False
    Public Property OrderShippedOn As Date = Nothing
    Public Property OrderPaid As Boolean = False
    Public Property OrderPaidOn As Date = Nothing
    Public Property TransactionID As String
End Class

사용.

Public Function GetCurrentOrders() As IEnumerable(Of OrderDCTyping)
    Try
        Using db As New DataAccess
            With db
                .QueryType = CmdType.StoredProcedure
                .Query = "[Desktop].[CurrentOrders]"
                Using _Results = .GetResults()
                    If _Results IsNot Nothing Then
                        _Qry = (From row In _Results.Cast(Of DbDataRecord)()
                                    Select New OrderDCTyping() With {
                                        .OrderID = Common.IsNull(Of Long)(row, 0, 0),
                                        .OrderTrackingNumber = Common.IsNull(Of String)(row, 1, String.Empty),
                                        .OrderShipped = Common.IsNull(Of Boolean)(row, 2, False),
                                        .OrderShippedOn = Common.IsNull(Of Date)(row, 3, Nothing),
                                        .OrderPaid = Common.IsNull(Of Boolean)(row, 4, False),
                                        .OrderPaidOn = Common.IsNull(Of Date)(row, 5, Nothing),
                                        .TransactionID = Common.IsNull(Of String)(row, 6, String.Empty)
                                    }).ToList()
                    Else
                        _Qry = Nothing
                    End If
                End Using
                Return _Qry
            End With
        End Using
    Catch ex As Exception
        Return Nothing
    End Try
End Function

언급URL : https://stackoverflow.com/questions/1464883/how-can-i-easily-convert-datareader-to-listt

반응형