Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Return DataTable And Using Entity Framework

4.90/5 (8 votes)
5 Apr 2020CPOL2 min read 45K   339  
Select data as DataTable object for a database using Entity Framework
To get back the results in the form of a DataTable object in an application using Oracle database and Entity Framework EDMX, I needed to select data from unmapped shared views with an existing DB context instance. To do this, I wrote a small extension method for DbContext object as explained in this post.

Background

At a particular situation in an application, we had to get back the results in the form of a DataTable object. The application was using the Oracle database and Entity Framework EDMX. We had to select data from unmapped shared views with an existing DB context instance. To do so, I actually had written a little extension method for DbContext object as explained below.

Extension Method

We are going to create extension methods for DbContext considering both Entity Framework and Entity Framework Core. The extension methods will be invoked with:

  • SQL query string
  • Optional DbParameter objects, in case of use of parameterized query

Let's start writing the code.

Entity Framework

C#
using System.Data;
using System.Data.Common;
using System.Data.Entity;

public static class DbContextExtensions
{
    /*
     * need
        Only EntityFramework
     */
    public static DataTable DataTable(this DbContext context, string sqlQuery, 
                                      params DbParameter[] parameters)
    {
        DataTable dataTable = new DataTable();
        DbConnection connection = context.Database.Connection;
        DbProviderFactory dbFactory = DbProviderFactories.GetFactory(connection);
        using (var cmd = dbFactory.CreateCommand())
        {
            cmd.Connection = connection;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sqlQuery;
            if (parameters != null)
            {
                foreach (var item in parameters)
                {
                    cmd.Parameters.Add(item);
                }
            }
            using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
            {
                adapter.SelectCommand = cmd;
                adapter.Fill(dataTable);
            }
        }
        return dataTable;
    }
}

Entity Framework Core

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.Common;
using Microsoft.EntityFrameworkCore;

public static class DbContextExtensions
{
    /*
     * need
        Microsoft.EntityFrameworkCore
        Microsoft.EntityFrameworkCore.Relational
     */
    public static DataTable DataTable(this DbContext context, 
           string sqlQuery, params DbParameter[] parameters)
    {
        DataTable dataTable = new DataTable();
        DbConnection connection = context.Database.GetDbConnection();
        DbProviderFactory dbFactory = DbProviderFactories.GetFactory(connection);
        using (var cmd = dbFactory.CreateCommand())
        {
            cmd.Connection = connection;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sqlQuery;
            if (parameters != null)
            {
                foreach (var item in parameters)
                {
                    cmd.Parameters.Add(item);
                }
            }
            using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
            {
                adapter.SelectCommand = cmd;
                adapter.Fill(dataTable);
            }
        }
        return dataTable;
    }
}

I tried to put the code to a common project, it actually depends on Microsoft.EntityFrameworkCore.Relational. By default, it is auto including with any provider DLL like Microsoft.EntityFrameworkCore.SqlServer.

Using the Extension Method

Using Regular Query

C#
var db = new MopDb();
DataTable allUser = db.DataTable("SELECT * FROM [dbo].[tbl_test_role]");

Using Parameterized Query

C#
var db = new MopDb();

/*stored procedure*/
DataTable searchUser = db.DataTable(
    "EXEC sp_test_role @name = @paramName",
    new SqlParameter("paramName", SqlDbType.NVarChar) { Value = "sa" }
);

/*select query*/
DataTable likeUser = db.DataTable(
    "SELECT * FROM [dbo].[tbl_test_role] WHERE [name] LIKE '%' + @paramName +'%'",
    new SqlParameter("paramName", SqlDbType.NVarChar) { Value = "a" }
);

We are executing both stored procedures and queries, I believe functions will also work.

DbParameter Names for Different DB

  • SqlServer: SqlParameter
  • Oracle: OracleParameter
  • MySql: MySqlParameter
  • PostgreSql: NpgsqlParameter

Manage NULL Value

If we want to pass NULL value as parameterized query, we can mange things at two places

SqlParameter Level

C#

C#
int? isActive = 1;
var param = new SqlParameter("paramIsActive", SqlDbType.Bit) { Value = isActive ?? (object)DBNull.Value, IsNullable = true }

Extension Method

Added foreach loop just at the start of the method

C#

C#
public static DataTable DataTable(this DbContext context, string sqlQuery, params DbParameter[] parameters)
{
    /*added to manage null values*/
    foreach (var parameter in parameters.Where(x => x.Value == null)) 
    {
        parameter.Value = DBNull.Value;
    }
}

Use Source Code With SQL-Server Db

Db Objects

Create Db Objects

SQL
CREATE TABLE [dbo].[tbl_test_role] (
    [id]   INT           IDENTITY (1, 1) NOT NULL,
    [name] NVARCHAR (50) NOT NULL,
    [details] NVARCHAR (150) NULL,
    PRIMARY KEY CLUSTERED ([id] ASC)
);
INSERT INTO [dbo].[tbl_test_role] (name)
VALUES ('admin'), ('sa'), ('user');


CREATE PROCEDURE sp_test_role @name nvarchar(30)
AS
BEGIN
    SELECT * FROM [dbo].[tbl_test_role]
    WHERE [name] = @name;
END;

Drop Db Objects If Needed

SQL
DROP TABLE [dbo].[tbl_test_role];
DROP PROCEDURE sp_test_role;

Solution And Projects

It is a Visual Studio 2017 solution:

  • WithEf is .NET Framework 4.5
  • WithEfCore is .NET Core 2.2

Change Connection Strings

App.config at WithEf:

XML
<connectionStrings>
  <add name="MopDbConnection" connectionString="Data Source=10.10.15.13\DB002;
   Initial Catalog=TESTDB; PASSWORD=dhaka; USER ID=FSTEST;"
   providerName="System.Data.SqlClient" />
</connectionStrings>

appsettings.json at WithEfCore:

JavaScript
"ConnectionStrings": {
  "MopDbConnection": "server=10.10.15.13\\DB002;database=TESTDB;
                      user id=FSTEST;password=dhaka"
}

Other Options

This does not use an entity, in fact, it goes around the entity using traditional SQL. Entity Framework supposed to maintain the state between the database and local data. In that case, we can convert a selected data list to a DataTable using this piece of code, Conversion Between DataTable and List in C#.

Limitations

The code may throw unexpected errors for untested inputs. If any, just let me know.

What is Next?

While working with the post, I found a thing called LINQ to SQL ObjectQuery.ToTraceString().

Going to work with it.

History

  • 5th April, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)