2017-02-07 7 views
9

У меня есть хранимая процедура на SQL Server, которая принимает тип таблицы, заданный пользователем. Я следую ответом из этого сообщения Bulk insert from C# list into SQL Server into multiple tables with foreign key constaints о том, как отправить DataTable в хранимую процедуру в SQL.Использование DataTable в .NET Core

Но когда я создаю DataTable table = new DataTable();, я получаю сообщение об ошибке DataTable does not contain a constructor that takes 0 arguments.

Я нашел это https://github.com/VahidN/EPPlus.Core/issues/4 который в основном говорит DataTable больше не поддерживается в .NET Core. Ну что теперь? как создать DataTable (или его замену)? как отправить тип таблицы, заданный пользователем, на SQL Server на .NET Core?

ответ

1

У меня была такая же проблема, что вы не можете создать DataTable и, следовательно, просто свалить это на лист.

Недостаток поддержки DataTable в Core заставляет вас создавать сильно типизированные объекты, а затем прокручивать их и отображать их на выходе EPPlus.

Так очень простой пример заключается в следующем:

// Get your data directly from EF, 
// or from whatever other source into a list, 
// or Enumerable of the type 
List<MyEntity> data = _whateverService.GetData(); 

using (ExcelPackage pck = new ExcelPackage()) 
{ 
    // Create a new sheet 
    var newSheet = pck.Workbook.Worksheets.Add("Sheet 1"); 
    // Set the header: 
    newSheet.Cells["A1"].Value = "Column 1 - Erm ID?"; 
    newSheet.Cells["B1"].Value = "Column 2 - Some data"; 
    newSheet.Cells["C1"].Value = "Column 3 - Other data"; 

    row = 2; 
    foreach (var datarow in data) 
    { 
     // Set the data: 
     newSheet.Cells["A" + row].Value = datarow.Id; 
     newSheet.Cells["B" + row].Value = datarow.Column2; 
     newSheet.Cells["C" + row].Value = datarow.Cilumn3; 
     row++; 
    } 
} 

Итак, вы принимаете перечислимый источник и сильно типизированный объект, который вы можете сделать прямо из запроса EF, или модель представления или что-нибудь иначе, а затем перейдем к отображению.

Я использовал это, и производительность появляется - конечному пользователю - наравне с методом DataTable. Я не проверял источник, но меня не удивило бы, если метод DataTable просто делает то же самое внутри и перебирает каждую строку.

Вы можете создать метод расширения для использования генериков для передачи в списке объектов и использовать отражение, чтобы правильно отобразить его. Возможно, я посмотрю проект и посмотрю, могу ли я внести свой вклад.

Изменить, чтобы добавить:

В .NET Ядра оказывается, от вопросов трекера GitHub, что поддержка DataTable является довольно низко в списке приоритетов, и не ожидаем, что это в ближайшее время. Я думаю, что это также философский момент, так как концепция, как правило, вы пытаетесь использовать сильно типизированные объекты. Таким образом, раньше вы могли запускать SQL Query в DataTable и запускать с этим ... Теперь вы должны запустить этот запрос в модель, либо непосредственно сопоставленную с таблицей с Entity Framework через DbSet, либо с помощью ModelBinding и проходящей тип запроса.

У вас тогда есть IQueryable<T>, который служит вашей строго типизированной заменой на DataTables. Чтобы быть справедливым в этом подходе, в 99% случаев это действенный и лучший подход ... Однако всегда будут недостатки DataTables, которые вызовут проблемы и должны быть проработаны!

Далее Edit

В ADO.NET вы можете конвертировать datareader в строго типизированных список объектов: How can I easily convert DataReader to List<T>? на имя, но один пример. С помощью этого списка вы можете сделать свое отображение оттуда.

Если вы хотите/должны использовать ASP.NET Core, который нацелен на ASP.NET Core framework, вам нужно будет это сделать. Если вы можете настроить таргетинг на Net 4.5, используя основной проект, тогда вы сможете использовать System.Data и вернуть DataTables - единственное предостережение, которое вам нужно использовать для работы с серверами Windows и IIS.

Вам действительно нужен полный каркас .Net Core? Вам нужно разместить на Linux? Если нет, и вам действительно нужны DataTables, просто настройте старую платформу.

+0

Спасибо. Но я не использую EF - я хочу придерживаться ADO.NET. Как передать это на SQL-сервер? выглядит как «грязное» решение простой проблемы ... – developer82

+0

Поддерживает ли ASP.NET Core поддержку ADO.NET? Не удается найти его в документах как быстрый поиск ... – RemarkLima

+0

EF построен на ADO.NET AFAIK, так что я думаю, да? – developer82

7

В качестве значения параметра SQL можно использовать DbDataReader. Итак, идея состоит в том, чтобы преобразовать IEnumerable<T> в DbDataReader.

public class ObjectDataReader<T> : DbDataReader 
{ 
    private bool _iteratorOwned; 
    private IEnumerator<T> _iterator; 
    private IDictionary<string, int> _propertyNameToOrdinal = new Dictionary<string, int>(); 
    private IDictionary<int, string> _ordinalToPropertyName = new Dictionary<int, string>(); 
    private Func<T, object>[] _getPropertyValueFuncs; 

    public ObjectDataReader(IEnumerable<T> enumerable) 
    { 
     if (enumerable == null) throw new ArgumentNullException(nameof(enumerable)); 

     _iteratorOwned = true; 
     _iterator = enumerable.GetEnumerator(); 
     _iterator.MoveNext(); 
     Initialize(); 
    } 

    public ObjectDataReader(IEnumerator<T> iterator) 
    { 
     if (iterator == null) throw new ArgumentNullException(nameof(iterator)); 

     _iterator = iterator;  
     Initialize(); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing && _iteratorOwned) 
     { 
      if(_iterator != null) 
       _iterator.Dispose(); 
     } 

     base.Dispose(disposing); 
    } 

    private void Initialize() 
    { 
     int ordinal = 0; 
     var properties = typeof(T).GetProperties(); 
     _getPropertyValueFuncs = new Func<T, object>[properties.Length]; 
     foreach (var property in properties) 
     { 
      string propertyName = property.Name; 
      _propertyNameToOrdinal.Add(propertyName, ordinal); 
      _ordinalToPropertyName.Add(ordinal, propertyName); 

      var parameterExpression = Expression.Parameter(typeof(T), "x"); 
      var func = (Func<T, object>)Expression.Lambda(Expression.Convert(Expression.Property(parameterExpression, propertyName), typeof(object)), parameterExpression).Compile(); 
      _getPropertyValueFuncs[ordinal] = func; 

      ordinal++; 
     } 
    } 

    public override object this[int ordinal] 
    { 
     get 
     { 
      return GetValue(ordinal); 
     } 
    } 

    public override object this[string name] 
    { 
     get 
     { 
      return GetValue(GetOrdinal(name)); 
     } 
    } 

    public override int Depth => 1; 

    public override int FieldCount => _ordinalToPropertyName.Count; 

    public override bool HasRows => true; 

    public override bool IsClosed 
    { 
     get 
     { 
      return _iterator != null; 
     } 
    } 

    public override int RecordsAffected 
    { 
     get 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public override bool GetBoolean(int ordinal) 
    { 
     return (bool)GetValue(ordinal); 
    } 

    public override byte GetByte(int ordinal) 
    { 
     return (byte)GetValue(ordinal); 
    } 

    public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) 
    { 
     throw new NotImplementedException(); 
    } 

    public override char GetChar(int ordinal) 
    { 
     return (char)GetValue(ordinal); 
    } 

    public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) 
    { 
     throw new NotImplementedException(); 
    } 

    public override string GetDataTypeName(int ordinal) 
    { 
     throw new NotImplementedException(); 
    } 

    public override DateTime GetDateTime(int ordinal) 
    { 
     return (DateTime)GetValue(ordinal); 
    } 

    public override decimal GetDecimal(int ordinal) 
    { 
     return (decimal)GetValue(ordinal); 
    } 

    public override double GetDouble(int ordinal) 
    { 
     return (double)GetValue(ordinal); 
    } 

    public override IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public override Type GetFieldType(int ordinal) 
    { 
     var value = GetValue(ordinal); 
     if (value == null) 
      return typeof(object); 

     return value.GetType(); 
    } 

    public override float GetFloat(int ordinal) 
    { 
     return (float)GetValue(ordinal); 
    } 

    public override Guid GetGuid(int ordinal) 
    { 
     return (Guid)GetValue(ordinal); 
    } 

    public override short GetInt16(int ordinal) 
    { 
     return (short)GetValue(ordinal); 
    } 

    public override int GetInt32(int ordinal) 
    { 
     return (int)GetValue(ordinal); 
    } 

    public override long GetInt64(int ordinal) 
    { 
     return (long)GetValue(ordinal); 
    } 

    public override string GetName(int ordinal) 
    { 
     string name; 
     if (_ordinalToPropertyName.TryGetValue(ordinal, out name)) 
      return name; 

     return null; 
    } 

    public override int GetOrdinal(string name) 
    { 
     int ordinal; 
     if (_propertyNameToOrdinal.TryGetValue(name, out ordinal)) 
      return ordinal; 

     return -1; 
    } 

    public override string GetString(int ordinal) 
    { 
     return (string)GetValue(ordinal); 
    } 

    public override object GetValue(int ordinal) 
    { 
     var func = _getPropertyValueFuncs[ordinal]; 
     return func(_iterator.Current); 
    } 

    public override int GetValues(object[] values) 
    { 
     int max = Math.Min(values.Length, FieldCount); 
     for (var i = 0; i < max; i++) 
     { 
      values[i] = IsDBNull(i) ? DBNull.Value : GetValue(i); 
     } 

     return max; 
    } 

    public override bool IsDBNull(int ordinal) 
    { 
     return GetValue(ordinal) == null; 
    } 

    public override bool NextResult() 
    { 
     return false; 
    } 

    public override bool Read() 
    { 
     return _iterator.MoveNext(); 
    } 
} 

Затем, вы можете использовать этот класс:

static void Main(string[] args) 
{ 
    Console.WriteLine("Hello World!"); 
    string connectionString = "Server=(local);Database=Sample;Trusted_Connection=True;"; 

    using (var connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 

     using (var command = connection.CreateCommand()) 
     { 
      command.CommandType = System.Data.CommandType.StoredProcedure; 
      command.CommandText = "procMergePageView"; 

      var p1 = command.CreateParameter(); 
      command.Parameters.Add(p1);  
      p1.ParameterName = "@Display"; 
      p1.SqlDbType = System.Data.SqlDbType.Structured; 
      var items = PageViewTableType.Generate(100); 
      using (DbDataReader dr = new ObjectDataReader<PageViewTableType>(items)) 
      { 
       p1.Value = dr; 
       command.ExecuteNonQuery(); 
      }  
     } 
    } 
} 

class PageViewTableType 
{ 
    // Must match the name of the column of the TVP 
    public long PageViewID { get; set; } 

    // Generate dummy data 
    public static IEnumerable<PageViewTableType> Generate(int count) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      yield return new PageViewTableType { PageViewID = i }; 
     } 
    } 
} 

В сценарии SQL:

CREATE TABLE dbo.PageView 
(
    PageViewID BIGINT NOT NULL CONSTRAINT pkPageView PRIMARY KEY CLUSTERED, 
    PageViewCount BIGINT NOT NULL 
); 
GO 

CREATE TYPE dbo.PageViewTableType AS TABLE 
(
    PageViewID BIGINT NOT NULL 
); 
GO 

CREATE PROCEDURE dbo.procMergePageView 
    @Display dbo.PageViewTableType READONLY 
AS 
BEGIN 
    MERGE INTO dbo.PageView AS T 
    USING @Display AS S 
    ON T.PageViewID = S.PageViewID 
    WHEN MATCHED THEN UPDATE SET T.PageViewCount = T.PageViewCount + 1 
    WHEN NOT MATCHED THEN INSERT VALUES(S.PageViewID, 1); 
END 

Кстати, я написал blog post about the ObjectDataReader<T>

+0

Спасибо. У вас есть код, который настроен на C# 6? – developer82

+0

Я сделал несколько исправлений. Скажите, если у вас есть ошибки компиляции. – meziantou

+0

Спасибо. Я получаю несколько ошибок. Первый: 'Неверный термин выражения 'throw'' – developer82

1

Есть 2 решения этой проблемы. Один использует DbDataReader, как @meziantou предложил в своем ответе, и было приятно предоставить общий метод, который преобразует IEnumerable<T> в DbDataReader.

Другое решение я нашел использовал SqlDataRecord, поэтому я пишу это здесь (используйте все, что вы посчитаете нужным для ваших нужд):

SQL Server таблицы:

CREATE TABLE [dbo].[Users](
    [UserId] [int] IDENTITY(1,1) NOT NULL, 
    [FirstName] [nvarchar](50) NULL, 
    [LastNAme] [nvarchar](50) NULL, 
CONSTRAINT [PK_USers] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

User-Defined Таблица Тип:

CREATE TYPE [dbo].[TblUser] AS TABLE(
    [FirstName] [nvarchar](50) NULL, 
    [LastNAme] [nvarchar](50) NULL 
) 

.NET код Core:

var db = new SqlConnection("Server=localhost; Database=Test; User Id=test; Password=123456;"); 

List<SqlDataRecord> users = new List<SqlDataRecord>(); 

SqlMetaData mDataFirstName = new SqlMetaData("FirstName", SqlDbType.NVarChar, 50); 
SqlMetaData mDataLastName = new SqlMetaData("LastName", SqlDbType.NVarChar, 50); 

SqlDataRecord user1 = new SqlDataRecord(new []{ mDataFirstName, mDataLastName }); 
user1.SetString(0, "Ophir"); 
user1.SetString(1, "Oren"); 
users.Add(user1); 

SqlParameter param = new SqlParameter("@Users", SqlDbType.Structured) 
{ 
    TypeName = "TblUser", 
    Value = users 
}; 

Dictionary<string, object> values = new Dictionary<string, object>(); 
values.Add("@Users", param); 


db.Open(); 
using (var command = db.CreateCommand()) 
{ 
    command.CommandType = System.Data.CommandType.StoredProcedure; 
    command.CommandText = "stp_Users_Insert"; 

    var p1 = command.CreateParameter(); 
    command.Parameters.Add(p1); 
    p1.ParameterName = "@Users"; 
    p1.SqlDbType = System.Data.SqlDbType.Structured; 
    p1.Value = users; 
    command.ExecuteNonQuery(); 
} 
0

Developer82,

Я нахожусь в той же ситуации, в которой я хочу использовать ядро ​​.net, но недоступность DataTable, набор данных облом. поскольку вы ссылаетесь на сообщение, которое использует список i, я подумал, что, возможно, цель состоит в том, чтобы получить список C# в базе данных самым чистым способом. Если это цель, это может помочь.

Я использовал Dapper, расположенный here в различных проектах. Он поддерживается int .netcore. ниже - небольшое консольное приложение, которое занимает заполненный список C# и вставляет его в БД, а затем выдает выбор в этой таблице для вывода результатов на консоль.

using System; 
using System.Data; 
using Dapper; 
using System.Data.Common; 
using System.Data.SqlClient; 
using System.Collections.Generic; 

namespace TestConsoleApp 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<DataItemDTO> dataItems = GetDataItems(); 

      var _selectSql = @"SELECT CustomerId, Name, BalanceDue from [dbo].[CustomerAccount]"; 

      var _insertSql = @"INSERT INTO [dbo].[CustomerAccount] 
             ([CustomerId] 
             ,[Name] 
             ,[BalanceDue]) 
           VALUES 
             (@CustomerId 
             ,@Name 
             ,@BalanceDue)"; 



      using (IDbConnection cn = new SqlConnection(@"Server=localhost\xxxxxxx;Database=xxxxdb;Trusted_Connection=True;")) 
      { 
       var rows = cn.Execute(_insertSql, dataItems,null,null,null); 

       dataItems.Clear(); 

       var results = cn.Query<DataItemDTO>(_selectSql); 

       foreach (var item in results) 
       { 
        Console.WriteLine("CustomerId: {0} Name {1} BalanceDue {2}", item.CustomerId.ToString(), item.Name, item.BalanceDue.ToString()); 
       } 


      } 

      Console.WriteLine("Press any Key"); 
      Console.ReadKey(); 

     } 

     private static List<DataItemDTO> GetDataItems() 
     { 
      List<DataItemDTO> items = new List<DataItemDTO>(); 

      items.Add(new DataItemDTO() { CustomerId = 1, Name = "abc1", BalanceDue = 11.58 }); 
      items.Add(new DataItemDTO() { CustomerId = 2, Name = "abc2", BalanceDue = 21.57 }); 
      items.Add(new DataItemDTO() { CustomerId = 3, Name = "abc3", BalanceDue = 31.56 }); 
      items.Add(new DataItemDTO() { CustomerId = 4, Name = "abc4", BalanceDue = 41.55 }); 
      items.Add(new DataItemDTO() { CustomerId = 5, Name = "abc5", BalanceDue = 51.54 }); 
      items.Add(new DataItemDTO() { CustomerId = 6, Name = "abc6", BalanceDue = 61.53 }); 
      items.Add(new DataItemDTO() { CustomerId = 7, Name = "abc7", BalanceDue = 71.52 }); 
      items.Add(new DataItemDTO() { CustomerId = 8, Name = "abc8", BalanceDue = 81.51 }); 

      return items; 
     } 
    } 
} 

Надеюсь, этот образец кода поможет.

спасибо.

1

@meziantou. Мне нравится ваш ответ, но в вашей реализации есть ошибка. Первая проблема заключалась в том, что MoveNext вызывался в конструкторе, что заставляло бы любую итерацию читателя всегда пропускать первое значение. Как только я удалил это, я обнаружил, почему это было сделано в первую очередь. Я изменил GetFieldType, чтобы использовать информацию типа, вместо того, чтобы читать тип из значения, который решил эту проблему. Опять же, действительно отличный ответ от вас. Благодарю вас за публикацию. Вот моя фиксированная версия ObjectDataReader.

public class ObjectDataReader<T> : DbDataReader 
    { 
     private bool _iteratorOwned; 
     private IEnumerator<T> _iterator; 
     private IDictionary<string, int> _propertyNameToOrdinal = new Dictionary<string, int>(); 
     private IDictionary<int, string> _ordinalToPropertyName = new Dictionary<int, string>(); 
     private PropertyInfoContainer[] _propertyInfos; 

     class PropertyInfoContainer 
     { 
      public Func<T, object> EvaluatePropertyFunction { get; set; } 
      public Type PropertyType { get; set; } 
      public string PropertyName { get; set; } 
      public PropertyInfoContainer(string propertyName 
       , Type propertyType 
       , Func<T, object> evaluatePropertyFunction) 
      { 
       this.PropertyName = propertyName; 
       this.PropertyType = propertyType; 
       this.EvaluatePropertyFunction = evaluatePropertyFunction; 
      } 
     } 

     public ObjectDataReader(IEnumerable<T> enumerable) 
     { 
      if (enumerable == null) throw new ArgumentNullException(nameof(enumerable)); 

      _iteratorOwned = true; 
      _iterator = enumerable.GetEnumerator(); 
      //_iterator.MoveNext(); 
      Initialize(); 
     } 

     public ObjectDataReader(IEnumerator<T> iterator) 
     { 
      if (iterator == null) throw new ArgumentNullException(nameof(iterator)); 

      _iterator = iterator; 
      Initialize(); 
     } 

     protected override void Dispose(bool disposing) 
     { 
      if (disposing && _iteratorOwned) 
      { 
       if (_iterator != null) 
        _iterator.Dispose(); 
      } 

      base.Dispose(disposing); 
     } 

     private void Initialize() 
     { 
      int ordinal = 0; 
      var properties = typeof(T).GetProperties(); 
      _propertyInfos = new PropertyInfoContainer[properties.Length]; 
      foreach (var property in properties) 
      { 
       string propertyName = property.Name; 
       _propertyNameToOrdinal.Add(propertyName, ordinal); 
       _ordinalToPropertyName.Add(ordinal, propertyName); 

       var parameterExpression = Expression.Parameter(typeof(T), "x"); 
       var func = (Func<T, object>)Expression.Lambda(Expression.Convert(Expression.Property(parameterExpression, propertyName), typeof(object)), parameterExpression).Compile(); 
       _propertyInfos[ordinal] = new PropertyInfoContainer(property.Name 
        , property.PropertyType 
        , func); 

       ordinal++; 
      } 
     } 

     public override object this[int ordinal] 
     { 
      get 
      { 
       return GetValue(ordinal); 
      } 
     } 

     public override object this[string name] 
     { 
      get 
      { 
       return GetValue(GetOrdinal(name)); 
      } 
     } 

     public override int Depth => 1; 

     public override int FieldCount => _ordinalToPropertyName.Count; 

     public override bool HasRows => true; 

     public override bool IsClosed 
     { 
      get 
      { 
       return _iterator != null; 
      } 
     } 

     public override int RecordsAffected 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public override bool GetBoolean(int ordinal) 
     { 
      return (bool)GetValue(ordinal); 
     } 

     public override byte GetByte(int ordinal) 
     { 
      return (byte)GetValue(ordinal); 
     } 

     public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) 
     { 
      throw new NotImplementedException(); 
     } 

     public override char GetChar(int ordinal) 
     { 
      return (char)GetValue(ordinal); 
     } 

     public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) 
     { 
      throw new NotImplementedException(); 
     } 

     public override string GetDataTypeName(int ordinal) 
     { 
      throw new NotImplementedException(); 
     } 

     public override DateTime GetDateTime(int ordinal) 
     { 
      return (DateTime)GetValue(ordinal); 
     } 

     public override decimal GetDecimal(int ordinal) 
     { 
      return (decimal)GetValue(ordinal); 
     } 

     public override double GetDouble(int ordinal) 
     { 
      return (double)GetValue(ordinal); 
     } 

     public override IEnumerator GetEnumerator() 
     { 
      throw new NotImplementedException(); 
     } 

     public override Type GetFieldType(int ordinal) 
     {  
      // cannot handle nullable types, so get underlying type 
      var propertyType = 
           Nullable.GetUnderlyingType(_propertyInfos[ordinal].PropertyType) ?? _propertyInfos[ordinal].PropertyType; 

      return propertyType; 
     } 

     public override float GetFloat(int ordinal) 
     { 
      return (float)GetValue(ordinal); 
     } 

     public override Guid GetGuid(int ordinal) 
     { 
      return (Guid)GetValue(ordinal); 
     } 

     public override short GetInt16(int ordinal) 
     { 
      return (short)GetValue(ordinal); 
     } 

     public override int GetInt32(int ordinal) 
     { 
      return (int)GetValue(ordinal); 
     } 

     public override long GetInt64(int ordinal) 
     { 
      return (long)GetValue(ordinal); 
     } 

     public override string GetName(int ordinal) 
     { 
      string name; 
      if (_ordinalToPropertyName.TryGetValue(ordinal, out name)) 
       return name; 

      return null; 
     } 

     public override int GetOrdinal(string name) 
     { 
      int ordinal; 
      if (_propertyNameToOrdinal.TryGetValue(name, out ordinal)) 
       return ordinal; 

      return -1; 
     } 

     public override string GetString(int ordinal) 
     { 
      return (string)GetValue(ordinal); 
     } 

     public override object GetValue(int ordinal) 
     { 
      var func = _propertyInfos[ordinal].EvaluatePropertyFunction; 
      return func(_iterator.Current); 
     } 

     public override int GetValues(object[] values) 
     { 
      int max = Math.Min(values.Length, FieldCount); 
      for (var i = 0; i < max; i++) 
      { 
       values[i] = IsDBNull(i) ? DBNull.Value : GetValue(i); 
      } 

      return max; 
     } 

     public override bool IsDBNull(int ordinal) 
     { 
      return GetValue(ordinal) == null; 
     } 

     public override bool NextResult() 
     { 
      return false; 
     } 

     public override bool Read() 
     { 
      return _iterator.MoveNext(); 
     } 
    } 
7

DataTable теперь поддерживается в .NET CORE 2.0. См. Мой ответ на .Net Core how to implement SQLAdapter ./ DataTable function. Пример кода ниже работает в версии 2.0.

public static DataTable ExecuteDataTableSqlDA(SqlConnection conn, CommandType cmdType, string cmdText, SqlParameter[] cmdParms) 
{ 
    System.Data.DataTable dt = new DataTable(); 
    System.Data.SqlClient.SqlDataAdapter da = new SqlDataAdapter(cmdText, conn); 
    da.Fill(dt); 
    return dt; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^