20

I am using entity framework. There is one particular situation in my application where I have to use a stored procedure. Since there are a lot of SQL statements written in the SP, I don't want to re-write it in my C# code. I only need to get the result back in the form of a datatable. I have written a little bit of code but I am stuck at one point. Can someone complete the code below?

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.
}
user1640256
  • 1,691
  • 7
  • 25
  • 48
  • use mentioned link http://www.entityframeworktutorial.net/EntityFramework4.3/execute-stored-procedure-using-dbcontext.aspx – Dhaval Patel May 16 '14 at 14:16
  • I am using code first and I don't have an entity for the returned dataset. I need the result set as DataTable. That's where I have to perform operation on the dynamic data(data with different column names for the parameter passed). – user1640256 May 16 '14 at 14:22
  • @user1640256 I see from the code that you are 'getting' something from the database. I infer that you are doing a SELECT query. So you know fairly well what columns you are fetching from the DB. Can you just create an entity and map the stored procedure results to the entity. That should make your work quite simple. – Satwik Nadkarny May 16 '14 at 14:29
  • Column names are generated dynamically for the result set. That is the logic I don't have to write in the code behind. – user1640256 May 16 '14 at 14:41

6 Answers6

22

Thanks a lot guys. I solved it. Here is the solution:

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;
}
Tim
  • 88
  • 6
user1640256
  • 1,691
  • 7
  • 25
  • 48
  • 10
    Here is why I downvoted this: This does not use entity in fact goes around entity using traditional sql. This fly's in the face of entity which is supposed to maintain the state between the database and local data. – John Smith Jul 08 '15 at 18:00
  • 8
    There _are_ use cases for it though... Think about a legacy app where the db needs to be reused, but new code is being written (what I'm doing now actually). EF can be worked into many places, making life a LOT easier, but there are some legacy sprocs that return different columns (Pivoted tables). No way that I know of to map that to a Entity when your column names are dynamic. So sure. Not "EF-friendly"... but sometimes a necessity of real life – BLSully Sep 03 '15 at 14:17
  • 4
    Re: There are use cases for it though. Sure enough. But addressing the question which was "Returning datatable using entity framework" this is a bad answer. Also In general unless your are in a exception such as you mentioned this is not good code for people to work off of. See my answer below. – John Smith Sep 16 '15 at 17:17
  • 4
    John, I too am a string believer in ORMs but this is absolutely not a bad answer, indeed it is better in multiple ways than yours below. There are numerous very valid reasons for getting a DataTable directly or in general circumventing the EF entity layer whilst also conveniently using EF connection configuration. Upvoted – Tom Deloford Nov 05 '15 at 12:55
  • 1
    I was searching for the exact title of this question. I know In a some cases this answer may be a better fit, but John has a better answer for people that are trying to use Entity framework (which are the people that will most likely land here). – Dave Kelly Dec 10 '16 at 01:26
  • Shouldn't the check in your finally be `if (connectionState == ConnectionState.Open)`? – Peter Aug 30 '17 at 09:38
  • There is no need to close the connection explicitly. Wrapping the context in a using statement will close the connection when the context is disposed. – Hans Vonn Jan 16 '18 at 18:48
  • The whole point of utilizing EF as an ORM is absent here. I can't see the role of EF here - just ADO.NET using the `Connection` object provided by the `Database` property of the context which is totally unnecessary. – Kamran Jul 27 '19 at 18:06
  • I don't see entity framework in action in this solution. – Shahid Malik Jun 17 '21 at 07:31
12

This example will return a datatable object selecting data from EntityFramework.

I believe this is the best solution to the goal. However the problem with this solution is that each record is enumerated. You might want to filter the list first then run this from the list to avoid that.

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;
                     });
Ali
  • 3,373
  • 5
  • 42
  • 54
John Smith
  • 591
  • 4
  • 15
  • 3
    Thanks John, I was looking for a way that actually uses Entity Framework. – Dave Kelly Dec 10 '16 at 01:20
  • 2
    This is fine, but it doesn't at all answer this specific question. In your answer here, you have `datbase.Table`. You are working with an Entity in your case whereas the question is related to a datatable where the properties are unknown. With a DataTable, you can observe the returned dataset including its column count and names. And, of course, the data. If you are using E.F. and Entities, mostly LinqToSQL or LinqToEntities, amongst thousands of lines of code, but have ONE instances where you need a DataTable to observe an unknown dataset, your answer doesn't work at all. – Suamere Nov 20 '17 at 23:19
8

This solution is simple, very fast and easy to use.

Create a DbContext extension:

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;
            }
        }
    }
}

Examples:

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()
                    );
}
F.Sighinolfi
  • 132
  • 1
  • 4
2

Just improving the previous solution, now including generic parameters (not SQL Server specific) and mutiple resultsets support:

    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;
    }
Daniel Brilho
  • 190
  • 2
  • 5
  • 8
    You are copying and pasting this answer to multiple questions([1](http://stackoverflow.com/a/35608488/189134), [2](http://stackoverflow.com/a/35608325/189134)). Please stop. Answers aren't for dumping the same block of text over and over. Instead, focus on the question and answer what is being asked. A long block of text doesn't do that. – Andy Feb 24 '16 at 17:24
-1

I added the following method into my DataContext class:

public async Task<DataTable> ExecReturnQuery(string query)
    {
        using (var command = this.Database.GetDbConnection().CreateCommand())
        {
            command.CommandText = query;

            this.Database.OpenConnection();

            using (var result = await command.ExecuteReaderAsync())
            {
                var table = new DataTable();
                table.Load(result);

                // returning DataTable (instead of DbDataReader), cause can't use DbDataReader after CloseConnection().
                this.Database.CloseConnection();

                return table;
            }
        }
    }

Then I call it from any class (where I'm injecting DataContext class) like so:

DataTable myTableRecords = await _dataContext.ExecReturnQuery("SELECT * FROM MyTable");

Mimina
  • 2,603
  • 2
  • 29
  • 21
-2

I just mixed the answers. this code run a dynamic query and convert result to dictionary list.

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]);
}
MSS
  • 3,520
  • 24
  • 29