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
'programing' 카테고리의 다른 글
명령줄의 스크립트에서 함수를 실행하려면 어떻게 해야 합니까? (0) | 2023.04.19 |
---|---|
핵심 데이터:엔티티의 모든 인스턴스를 삭제하는 가장 빠른 방법 (0) | 2023.04.19 |
SQL Server 데이터베이스 버전 제어 방법 (0) | 2023.04.19 |
WPF에서 UI(메인) 스레드에 안전하게 액세스 (0) | 2023.04.19 |
배시 템플릿:Bash를 사용하여 템플릿에서 구성 파일을 작성하는 방법 (0) | 2023.04.19 |