>  기사  >  백엔드 개발  >  .Net Core에서 직접 SQL 문을 실행하고 DataTable을 생성하는 구현 방법

.Net Core에서 직접 SQL 문을 실행하고 DataTable을 생성하는 구현 방법

高洛峰
高洛峰원래의
2016-12-20 14:24:062356검색

.net 코어는 SQL 문을 실행할 수 있지만 강력한 형식의 반환 결과만 생성할 수 있습니다. 예를 들어 var blogs = context.Blogs.FromSql("SELECT * FROM dbo.Blogs").ToList()입니다. DataSet 및 DataTable과 같은 약한 유형을 반환하는 것은 허용되지 않습니다. 이러한 이유로 DataTable이 .net 코어에 구현되지 않았을 수 있지만 DataTable은 계속 사용될 수 있습니다. 여기에는 사용자가 SQL과 유사한 명령문을 직접 작성한 다음 이를 실행하고 테이블에 표시할 수 있도록 하는 데이터 웨어하우스 요구 사항이 있습니다. 문장이 계속 바뀌기 때문에 사용자의 문장이 어떤 결과를 출력하는지 알 수 없고, 유형별로 정의할 수도 없어서 DataTable 메서드만 사용할 수 있습니다.

이전에는 .net 프레임워크에서 dataadpater를 통해 데이터 테이블을 쉽게 채운 다음 데이터 테이블의 데이터를 클라이언트에 푸시하여 표시할 수 있었습니다. 그러나 .net 코어에는 DataTable과 DataSet이 없으며 MicroDataTable을 직접 구현할 수만 있습니다.

여기에서도 DataTable 메서드를 따릅니다. MicroDataTable의 열은 MicroDataColumn으로 정의되고 행은 MicroDataRow로 정의됩니다. 코드는 다음과 같습니다.

public class MicroDataTable
{ /// <summary>
/// 整个查询语句结果的总条数,而非本DataTable的条数
/// </summary>
public int TotalCount { get; set; }
public List<MicroDataColumn> Columns { get; set; } = new List<MicroDataColumn>();
public List<MicroDataRow> Rows { get; set; } = new List<MicroDataRow>();
public MicroDataColumn[] PrimaryKey { get; set; }
public MicroDataRow NewRow()
{
return new MicroDataRow(this.Columns, new object[Columns.Count]);
}
}
public class MicroDataColumn
{
public string ColumnName { get; set; }
public Type ColumnType { get; set; }
}
public class MicroDataRow
{
private object[] _ItemArray;
public List<MicroDataColumn> Columns { get; private set; }
public MicroDataRow(List<MicroDataColumn> columns, object[] itemArray)
{
this.Columns = columns;
this._ItemArray = itemArray;
}
public object this[int index]
{
get { return _ItemArray[index]; }
set { _ItemArray[index] = value; }
}
public object this[string columnName]
{
get
{
int i = 0;
foreach (MicroDataColumn column in Columns)
{
if (column.ColumnName == columnName)
break;
i++;
}
return _ItemArray[i];
}
set
{
int i = 0;
foreach (MicroDataColumn column in Columns)
{
if (column.ColumnName == columnName)
break;
i++;
}
_ItemArray[i] = value;
}
}
}

페이징의 경우 TotalCount 속성은 쿼리문이 데이터베이스에 쿼리한 모든 레코드의 수를 참조하는 반면, MicroDataTable은 현재 페이지의 기록입니다.

데이터베이스에서 DataTable을 얻으려면 SqlHelper와 유사한 메서드를 사용하여 DbContext의 ExecuteDataTable 확장 메서드를 작성하고, SQL 문과 SQL 문의 매개 변수를 전달하고 MicroDataTable을 생성합니다.

public static MicroDataTable ExecuteDataTable(this DbContext context, string sql, params object[] parameters)
{
var concurrencyDetector = context.Database.GetService<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
var rawSqlCommand = context.Database.GetService<IRawSqlCommandBuilder>().Build(sql, parameters);
RelationalDataReader query = rawSqlCommand.RelationalCommand.ExecuteReader(context.Database.GetService<IRelationalConnection>(), parameterValues: rawSqlCommand.ParameterValues);
return MicroDataTableHelper.FillDataTable(query.DbDataReader, 0, int.MaxValue);
}
}
public static MicroDataTable ExecuteDataTable(this DbContext context, string sql, int pageIndex, int pageSize, params object[] parameters)
{
var concurrencyDetector = context.Database.GetService<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
var rawSqlCommand = context.Database.GetService<IRawSqlCommandBuilder>().Build(sql, parameters);
RelationalDataReader query = rawSqlCommand.RelationalCommand.ExecuteReader(context.Database.GetService<IRelationalConnection>(), parameterValues: rawSqlCommand.ParameterValues);
return MicroDataTableHelper.FillDataTable(query.DbDataReader, 0, int.MaxValue);
}
}

이 방법에는 여전히 일부 .net 프레임워크 핵심 기술이 필요합니다. 이 프로세스는 SQL 및 매개변수를 기반으로 기본 SQLCommand를 생성하고 ExecuteReader 메서드를 실행하여 DataReader를 반환한 다음 DataReader를 MicroDataTable에 채우는 것입니다. .net 코어의 IConcurrencyDetector에 대한 설명은 다음과 같습니다. 이 API는 Entity Framework Core 인프라를 지원하며 코드에서 직접 사용할 수 없습니다. 이 API는 향후 릴리스에서 변경되거나 제거될 수 있습니다. 먼저 이 방법으로만 구현할 수 있으며, ef.core가 변경될 수 있는지 또는 나중에 더 나은 방법이 제공될 수 있는지 확인할 수 있습니다.

위 프로그램의 끝에 MicroDataTableHelper.FillDataTable이라는 문장이 있습니다. 이 메서드의 주요 기능은 DataReader에서 MicroDataTable을 채우는 것입니다.

public static MicroDataTable FillDataTable(DbDataReader reader, int pageIndex, int pageSize)
{
bool defined = false;
MicroDataTable table = new MicroDataTable();
int index = 0;
int beginIndex = pageSize * pageIndex;
int endIndex = pageSize * (pageIndex + 1) - 1;
while (reader.Read())
{
object[] values = new object[reader.FieldCount];
if (!defined)
{
for (int i = 0; i < reader.FieldCount; i++)
{
MicroDataColumn column = new MicroDataColumn()
{
ColumnName = reader.GetName(i),
ColumnType = reader.GetFieldType(i)
};
table.Columns.Add(column);
}
defined = true;
}
if (index >= beginIndex && index <= endIndex)
{
reader.GetValues(values);
table.Rows.Add(new MicroDataRow(table.Columns, values));
}
index++;
}
table.TotalCount = index;
return table;
}

위 프로그램은 단계별로 작성되었으므로 그다지 효율적이지는 않습니다. 최근에는 시간이 부족하여 원래의 Datatable 로딩 방식을 분석하지 못했습니다. 앞으로 최적화하는 시간을 갖도록 하겠습니다.

다음은 참고용으로만 .net 프레임워크를 사용하여 datareader에서 datatable로 페이징 데이터를 가져오는 프로그램입니다. 당시 이 프로그램은 table.beginloaddata/endloaddata 메소드를 사용하여 효율성이 크게 향상되었습니다.

using (IDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
int fieldCount = reader.FieldCount;
for (int i = 0; i < fieldCount; i++)
{
table.Columns.Add(reader.GetName(i), reader.GetFieldType(i));
}
object[] values = new object[fieldCount];
int currentIndex = 0;
int startIndex = pageSize * pageIndex;
try
{
table.BeginLoadData();
while (reader.Read())
{
if (startIndex > currentIndex++)
continue;
if (pageSize > 0 && (currentIndex - startIndex) > pageSize)
break;
reader.GetValues(values);
table.LoadDataRow(values, true);
}
}
finally
{
table.EndLoadData();
try //lgy:由于连接阿里云ADS数据库cmd.Cancel()会报错,所以把错误忽略了。
{
cmd.Cancel();
}
catch
{ 
}
reader.Close();
}
}

위 내용은 .Net 코어에서 직접 SQL 문을 실행하고 DataTable을 생성하기 위해 편집자가 소개한 내용입니다. 또한 PHP 중국어 웹사이트를 지원해 주신 모든 분들께 감사드립니다!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.