【问题标题】:Returning datatable using entity framework使用实体框架返回数据表
【发布时间】:2014-07-05 00:51:30
【问题描述】:

我正在使用实体框架。在我的应用程序中有一种特殊情况,我必须使用存储过程。由于SP中写了很多SQL语句,我不想​​在我的C#代码中重写它。我只需要以数据表的形式返回结果。我已经写了一点代码,但我被困在一个点上。有人可以完成下面的代码吗?

using (dbContext.Database.Connection)
{
dbContext.Database.Connection.Open();
DbCommand cmdItems= dbContext.Database.Connection.CreateCommand();
cmdItems.CommandText = "GetAvailableItems";
cmdItems.CommandType = CommandType.StoredProcedure;
cmdItems.Parameters.Add(new SqlParameter("jobCardId", 100525));
//Need to write code below to populate a DataTable.
}

【问题讨论】:

  • 我首先使用代码,但我没有返回数据集的实体。我需要将结果集作为 DataTable。这就是我必须对动态数据(传递的参数具有不同列名的数据)执行操作的地方。
  • @user1640256 我从代码中看到您正在从数据库中“获取”一些东西。我推断您正在执行 SELECT 查询。所以你很清楚你从数据库中获取了哪些列。您能否只创建一个实体并将存储过程结果映射到实体。这应该会让你的工作变得非常简单。
  • 列名是为结果集动态生成的。这就是我后面代码里不用写的逻辑了。

标签: c# entity-framework ado.net-entity-data-model


【解决方案1】:

我只是混淆了答案。此代码运行动态查询并将结果转换为字典列表。

public List < Dictionary < string,object >> DataTableToDictionaryList(DataTable table) {
    List < Dictionary < string,
    object >> parentRow = new List < Dictionary < string,
    object >> ();
    Dictionary < string,
    object > childRow;
    foreach(DataRow row in table.Rows) {
        childRow = new Dictionary < string,
        object > ();
        foreach(DataColumn col in table.Columns) {
            childRow.Add(col.ColumnName, row[col]);
        }
        parentRow.Add(childRow);
    }
    return (parentRow);
}

List < Dictionary < string,object >> RunDynamicQuery(string sql, Dictionary < string, Object > parameters = null, int resultSet = 0, CommandType commandType = CommandType.Text) {
    // creates resulting dataset
    var resultDataSet = new DataSet();

    // creates a data access context (DbContext descendant)
    using(var context = new DataDbContext()) {
        // creates a Command 
        var conn = context.Database.Connection;
        var cmd = conn.CreateCommand();
        cmd.CommandType = commandType;
        cmd.CommandText = sql;

        if (parameters != null) {
            // adds all parameters
            foreach(var pr in parameters) {
                var p = cmd.CreateParameter();
                p.ParameterName = pr.Key;
                p.Value = pr.Value;
                cmd.Parameters.Add(p);
            }
        }

        try {
            // executes
            if (conn.State != ConnectionState.Open) {
                conn.Open();
            }

            var reader = cmd.ExecuteReader();

            // loop through all resultsets (considering that it's possible to have more than one)
            int currentResultSet = -1;
            do {
                currentResultSet++;
                //skip lower resultsets
                if (resultSet > currentResultSet) {
                    continue;
                }

                // loads the DataTable (schema will be fetch automatically)
                var tb = new DataTable();
                tb.Load(reader);
                resultDataSet.Tables.Add(tb);
                //make sure to get only one result set
                break;
            } while (! reader . IsClosed );

        }
        finally {
            // closes the connection
            context.Database.Connection.Close();
        }
    }

    return DataTableToDictionaryList(resultDataSet.Tables[0]);
}

【讨论】:

    【解决方案2】:

    非常感谢各位。我解决了。这是解决方案:

    using (var context = new DataBaseContext())
    {
        var dt = new DataTable();
        var conn = context.Database.Connection;
        var connectionState = conn.State;
        try
        {
            if (connectionState != ConnectionState.Open) conn.Open();
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "GetAvailableItems";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(new SqlParameter("jobCardId", 100525));
                using (var reader = cmd.ExecuteReader())
                {
                    dt.Load(reader);
                }
            }
        }
        catch (Exception ex)
        {
            // error handling
            throw;
        }
        finally
        {
            if (connectionState != ConnectionState.Closed) conn.Close();
        }
        return dt;
    }
    

    【讨论】:

    • 这就是我否决的原因:这不使用实体实际上使用传统 sql 绕过实体。这只苍蝇面对的是应该维护数据库和本地数据之间状态的实体。
    • 虽然有个用例......考虑一个遗留应用程序,其中需要重用数据库,但正在编写新代码(我在做什么现在实际上)。 EF 可以在很多地方使用,让生活变得更轻松,但是有一些遗留的存储过程会返回不同的列(数据透视表)。当您的列名是动态的时,我不知道将其映射到实体。这么确定。不是“对 EF 友好”……但有时是现实生活的必需品
    • 回复:虽然它有一些用例。果然。但是解决“使用实体框架返回数据表”的问题,这是一个糟糕的答案。此外,一般而言,除非您遇到异常,例如您提到的,否则这不是人们工作的好代码。请参阅下面的答案。
    • John,我也是 ORM 的字符串信徒,但这绝对不是一个糟糕的答案,事实上它在多个方面都比你下面的更好。直接获取 DataTable 或通常绕过 EF 实体层同时也方便地使用 EF 连接配置有许多非常正当的理由。赞成
    • 我正在寻找这个问题的确切标题。我知道在某些情况下,这个答案可能更合适,但对于尝试使用实体框架的人(这些人最有可能在这里登陆),John 有一个更好的答案。
    【解决方案3】:

    此示例将返回一个datatable 对象,从EntityFramework 中选择数据。

    我相信这是实现目标的最佳解决方案。但是,此解决方案的问题在于枚举了每条记录。您可能希望先过滤列表,然后从列表中运行它以避免这种情况。

    DataTable dt = new DataTable();
    (from rec in database.Table.AsEnumerable()
                         select new
                         {
                             id = rec.id,
                             name = rec.Name
                             //etc
                         }).Aggregate(table, (dt, r) =>
                         {
                             dt.Rows.Add(r.id, r.Name);
                             return dt;
                         });
    

    【讨论】:

    • 谢谢约翰,我正在寻找一种真正使用实体框架的方法。
    • 这很好,但它根本不能回答这个具体问题。在您的回答中,您有datbase.Table。在您的案例中,您正在使用实体,而问题与属性未知的数据表有关。使用 DataTable,您可以观察返回的数据集,包括其列数和名称。当然,还有数据。如果您在数千行代码中使用 E.F. 和实体,主要是 LinqToSQL 或 LinqToEntities,但有一个实例需要 DataTable 来观察未知数据集,那么您的答案根本不起作用。
    【解决方案4】:

    此解决方案简单、快速且易于使用。

    创建一个 DbContext 扩展:

    using System.Data;
    using System.Data.Common;
    using System.Data.Entity;
    ..
    ..
    public static class DbContextExtensions
    {
        public static DataTable DataTable(this DbContext context, string sqlQuery)
        {
            DbProviderFactory dbFactory = DbProviderFactories.GetFactory(context.Database.Connection);
    
            using (var cmd = dbFactory.CreateCommand())
            {
                cmd.Connection = context.Database.Connection;
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = sqlQuery;
                using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
                {
                    adapter.SelectCommand = cmd;
    
                    DataTable dt = new DataTable();
                    adapter.Fill(dt);
    
                    return dt;
                }
            }
        }
    }
    

    例子:

    using (MyDbContext db = new MyDbContext())
    {
        string query = db.Students.Where(o => o.Age > 20).ToString();
    
        DataTable dataTable = db.DataTable(query);
    
        ..
    
        DataTable dt = db.DataTable(
                             (  from o in db.Studets
                                where o.Age > 20
                                select o
                             ).ToString()
                        );
    }
    

    【讨论】:

    • 这看起来完全绕过了实体框架。
    【解决方案5】:

    只是改进了以前的解决方案,现在包括通用参数(不是特定于 SQL Server)和多个结果集支持:

        DataSet GetDataSet(string sql, CommandType commandType, Dictionary<string, Object> parameters)
        {
            // creates resulting dataset
            var result = new DataSet();
    
            // creates a data access context (DbContext descendant)
            using (var context = new MyDbContext())
            {
                // creates a Command 
                var cmd = context.Database.Connection.CreateCommand();
                cmd.CommandType = commandType;
                cmd.CommandText = sql;
    
                // adds all parameters
                foreach (var pr in parameters)
                {
                    var p = cmd.CreateParameter();
                    p.ParameterName = pr.Key;
                    p.Value = pr.Value;
                    cmd.Parameters.Add(p);
                }
    
                try
                {
                    // executes
                    context.Database.Connection.Open();
                    var reader = cmd.ExecuteReader();
    
                    // loop through all resultsets (considering that it's possible to have more than one)
                    do
                    {
                        // loads the DataTable (schema will be fetch automatically)
                        var tb = new DataTable();
                        tb.Load(reader);
                        result.Tables.Add(tb);
    
                    } while (!reader.IsClosed);
                }
                finally
                {
                    // closes the connection
                    context.Database.Connection.Close();
                }
            }
    
            // returns the DataSet
            return result;
        }
    

    【讨论】:

    • 您正在将此答案复制并粘贴到多个问题(12)。请停下。答案不是为了一遍又一遍地转储相同的文本块。相反,专注于问题并回答所提出的问题。长文本块不会这样做。
    猜你喜欢
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    相关资源
    最近更新 更多