在.NET Core中使用DataTable

dev*_*r82 13 c# datatable .net-core asp.net-core asp.net-core-1.0

我在SQL Server中有一个存储过程,它接受用户定义的表类型.我正在按照这篇文章的答案从C#list批量插入SQL Server到多个表,其中包含有关如何将DataTable发送到SQL中的存储过程的外键重量.

但是当我创建时,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(或者它的替代品是什么)?如何将用户定义的表类型发送到.NET Core上的SQL Server?

Joe*_*aly 13

.NET CORE 2.0现在支持DataTable.请参阅我在.Net Core的答案如何实现SQLAdapter ./ DataTable函数.以下示例代码适用于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;
}
Run Code Online (Sandbox Code Playgroud)


mez*_*tou 8

您可以使用a DbDataReader作为SQL参数的值.所以,想法是将IEnumerable<T>a 转换为a 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此类:

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 };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

顺便说一下,我写了一篇关于这篇文章博客文章ObjectDataReader<T>


Rem*_*ima 0

我遇到了同样的问题,您无法创建一个DataTable,因此只需将其转储到工作表中即可。

Core 中缺乏DataTable支持确实迫使您创建强类型对象,然后循环并将它们映射到 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++;
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,您将获取强类型对象的可枚举源,您可以直接从 EF 查询、视图模型或其他任何内容执行此操作,然后循环以映射它。

我已经使用过这个方法,对于最终用户来说,其性能与该方法相当DataTableDataTable我没有检查源代码,但如果该方法只是在内部执行相同的操作并循环遍历每一行,我不会感到惊讶。

您可以创建一个扩展方法来使用泛型传递对象列表并使用反射来正确映射它......也许我会查看该项目并看看我是否可以做出贡献。

编辑添加:

从 GitHub 问题跟踪器来看,在 .NET Core 中,DataTable支持在优先级列表中的位置相当靠后,并且不会很快出现。我认为这也是一个哲学点,因为这个概念通常是您尝试使用强类型对象。因此,过去您可以在 a 中运行 SQL 查询并使用它运行...现在,您应该将该查询运行到模型中,或者通过 aDataTable直接映射到表,或者使用并将类型传递到询问。Entity FrameworkDbSetModelBinding

然后你就有了一个IQueryable<T>which作为你的强类型替代品DataTables。公平地说,这种方法对于 99% 的情况来说是一种有效且更好的方法……但是,总会有一些时候,缺乏方法DataTables会导致问题,需要解决!

进一步编辑

ADO.NET可以将 a 转换datareader为强类型的对象列表: How can I easy conversion DataReader to List<T>? 仅举一例。通过此列表,您可以从那里进行映射。

如果您想/必须使用ASP.NET Core该目标ASP.NET Core framework,那么您必须这样做。如果您可以使用 Core 项目以 Net 4.5 为目标,那么您将能够使用System.Data并获得DataTables回报 - 唯一需要注意的是您必须使用 Windows 服务器和 IIS。

您真的需要完整的.Net Core框架吗?您需要在 Linux 上托管吗?如果不是,并且您确实需要数据表,则只需针对旧框架即可。