Pergunta

I am working with a system that has many stored procedures that need to be displayed. Creating entities for each of my objects is not practical.

Is it possible and how would I return a DataTable using ExecuteStoreQuery ?

public ObjectResult<DataTable> MethodName(string fileSetName) {
using (var dataContext = new DataContext(_connectionString))
{
var returnDataTable = ((IObjectContextAdapter)dataContext).ObjectContext.ExecuteStoreQuery<DataTable>("SP_NAME","SP_PARAM");
return returnDataTable;
}
Foi útil?

Solução

No, I don't think that'll work - Entity Framework is geared towards returning entities and isn't meant to return DataTable objects.

If you need DataTable objects, use straight ADO.NET instead.

Outras dicas

Yes it's possible, but it should be used for just dynamic result-set or raw SQL.

public DataTable ExecuteStoreQuery(string commandText, params Object[] parameters)
{
    DataTable retVal = new DataTable();
    retVal = context.ExecuteStoreQuery<DataTable>(commandText, parameters).FirstOrDefault();
    return retVal;
}

Edit: It's better to use classical ADO.NET to get the data model rather than using Entity Framework because most probably you cannot use DataTable even if you can run the method: context.ExecuteStoreQuery<DataTable>(commandText, parameters).FirstOrDefault();

ADO.NET Example:

public DataSet GetResultReport(int questionId)
{
    DataSet retVal = new DataSet();
    EntityConnection entityConn = (EntityConnection)context.Connection;
    SqlConnection sqlConn = (SqlConnection)entityConn.StoreConnection;
    SqlCommand cmdReport = new SqlCommand([YourSpName], sqlConn);
    SqlDataAdapter daReport = new SqlDataAdapter(cmdReport);
    using (cmdReport)
    {
        SqlParameter questionIdPrm = new SqlParameter("QuestionId", questionId);
        cmdReport.CommandType = CommandType.StoredProcedure;
        cmdReport.Parameters.Add(questionIdPrm);
        daReport.Fill(retVal);
    }
    return retVal;
}

This method uses the connection string from the entity framework to establish an ADO.NET connection, to a MySQL database in this example.

using MySql.Data.MySqlClient;

public DataSet GetReportSummary( int RecordID )
{
    var context = new catalogEntities();

    DataSet ds = new DataSet();
    using ( MySqlConnection connection = new MySqlConnection( context.Database.Connection.ConnectionString ) )
    {
        using ( MySqlCommand cmd = new MySqlCommand( "ReportSummary", connection ) )
        {
            MySqlDataAdapter adapter = new MySqlDataAdapter( cmd );
            adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
            adapter.SelectCommand.Parameters.Add( new MySqlParameter( "@ID", RecordID ) );
            adapter.Fill( ds );
        }
    }
    return ds;
}

Yes it can easily be done like this:

var table = new DataTable();
using (var ctx = new SomeContext())
{
    var cmd = ctx.Database.Connection.CreateCommand();
    cmd.CommandText = "Select Col1, Col2 from SomeTable"; 

    cmd.Connection.Open();
    table.Load(cmd.ExecuteReader());
}

By the rule, you shouldn't use a DataSet inside a EF application. But, if you really need to (for instance, to feed a report), that solution should work (it's EF 6 code):

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

The easiest way to return a DataTable using the EntityFramework is to do the following:

MetaTable metaTable = Global.DefaultModel.GetTable("Your EntitySetName");

For example:

MetaTable metaTable = Global.DefaultModel.GetTable("Employees");

Maybe your stored procedure could return a complex type? http://blogs.msdn.com/b/somasegar/archive/2010/01/11/entity-framework-in-net-4.aspx

In my Entity Framework based solution I need to replace one of my Linq queries with sql - for efficiency reasons. Also I want my results in a DataTable from one stored procedure so that I could create a table value parameter to pass into a second stored procedure. So:

  1. I'm using sql

  2. I don't want a DataSet

  3. Iterating an IEnumerable probably isn't going to cut it - for efficiency reasons

Also, I am using EF6, so I would prefer DbContext.SqlQuery over ObjectContext.ExecuteStoreQuery as the original poster requested.

However, I found that this just didn't work:

_Context.Database.SqlQuery<DataTable>(sql, parameters).FirstOrDefault();

This is my solution. It returns a DataTable that is fetched using an ADO.NET SqlDataReader - which I believe is faster than a SqlDataAdapter on read-only data. It doesn't strictly answer the question because it uses ADO.Net, but it shows how to do that after getting a hold of the connection from the DbContext

    protected DataTable GetDataTable(string sql, params object[] parameters)
    {
        //didn't work - table had no columns or rows
        //return Context.Database.SqlQuery<DataTable>(sql, parameters).FirstOrDefault();

        DataTable result = new DataTable();
        SqlConnection conn = Context.Database.Connection as SqlConnection;
        if(conn == null)
        {
            throw new InvalidCastException("SqlConnection is invalid for this database");
        }
        using (SqlCommand cmd = new SqlCommand(sql, conn))
        {
            cmd.Parameters.AddRange(parameters);
            conn.Open();
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                result.Load(reader);
            }
            return result;
        }
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top