268

With Entity Framework Core removingdbData.Database.SqlQuery<SomeModel> I can't find a solution to build a raw SQL Query for my full-text search query that will return the tables data and also the rank.

The only method I've seen to build a raw SQL query in Entity Framework Core is viadbData.Product.FromSql("SQL SCRIPT"); which isn't useful as I have no DbSet that will map the rank I return in the query.

Any Ideas???

steamrolla's user avatar
steamrolla
2,4912 gold badges32 silver badges40 bronze badges
askedFeb 25, 2016 at 15:44
David Harlow's user avatar
9
  • 60
    I will greatly miss the SqlQuery<T> and don't want to have to map custom classes to my DbContext when I really just need a simple DTO for a specific use case. I have created a user voice to request adding this feature back in to EF Core that anyone can vote up if they would like this feature back:data.uservoice.com/forums/…CommentedMar 28, 2016 at 16:35
  • 2
    Building on what @Devon just said, I spent way too long just now figuring out they are extension methods in Microsoft.EntityFrameworkCore.SqlServer. You'll need to add that to your project before getting these extension methods.CommentedSep 12, 2019 at 16:50
  • 13
    Sigh this seems like some kind of Architecture Astronaut decision: "the people shouldn't need to want this". I guess I have to install Dapper just for this case. Annoying.CommentedApr 29, 2020 at 12:25
  • 3
    @MattSanders - you're uservoice link seems to be dead in the meantime. Do you know where it went?CommentedApr 29, 2020 at 12:28
  • 2
    I have also installed Dapper just for this, using an extension method over the DbContext and it worked great.CommentedSep 22, 2021 at 18:25

23 Answers23

283

EF Core 8 and newer

TheSqlQuery method was added in EF Core 7.0 to support returning scalar values.

Starting from EF Core 8,this method will additionally support returning arbitrary types.


EF Core 3.0

You need to usekeyless entity types, previously known as query types:

This feature was added in EF Core 2.1 under the name of query types.In EF Core 3.0 the concept was renamed to keyless entity types. The[Keyless] Data Annotation became available in EFCore 5.0.

To use them you need to first mark your classSomeModel with[Keyless] data annotation or through fluent configuration with.HasNoKey() method call like below:

public DbSet<SomeModel> SomeModels { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder){    modelBuilder.Entity<SomeModel>().HasNoKey();}

After that configuration, you can use one of the methodsexplained here to execute your SQL query. For example you can use this one:

var result = context.SomeModels.FromSqlRaw("SQL SCRIPT").ToList();var result = await context.SomeModels.FromSql("SQL_SCRIPT").ToListAsync();

EF Core 2.1

If you're using EF Core 2.1 Release Candidate 1 available since 7 may 2018, you can take advantage of the proposed new feature which isquery types:

In addition to entity types, an EF Core model can contain query types,which can be used to carry out database queries against data thatisn't mapped to entity types.

When to use query type?

Serving as the return type for ad hoc FromSql() queries.

Mapping to database views.

Mapping to tables that do not have a primary key defined.

Mapping to queries defined in the model.

So you no longer need to do all the hacks or workarounds proposed as answers to your question. Just follow these steps:

First you defined a new property of typeDbQuery<T> whereT is the type of the class that will carry the column values of your SQL query. So in yourDbContext you'll have this:

public DbQuery<SomeModel> SomeModels { get; set; }

Secondly useFromSql method like you do withDbSet<T>:

var result = context.SomeModels.FromSql("SQL_SCRIPT").ToList();var result = await context.SomeModels.FromSql("SQL_SCRIPT").ToListAsync();

Also note thatDbContexts arepartial classes, so you can create one or more separate files to organize your 'raw SQL DbQuery' definitions as best suits you.

answeredMay 21, 2018 at 15:54
CodeNotFound's user avatar
Sign up to request clarification or add additional context in comments.

34 Comments

This answer should be the best solution when using EF Core 2.1 and above. 👍
Using CodeFirst this automatically created a table with all those properties, adding[NotMapped] to theSomeModels class does not work for me. Did I miss anything?
EF Core 3.0 deprecatesDbQuery in favor of just usingDbSet withkeyless entity types.
Just FYI, due to some bug in EF core 3.0, a code-first migration will still try to create a table even on entities marked with HasNoKey(). So you have to do also add .ToView(null). E.g.modelBuilder.Entity<MyData>().HasNoKey().ToView(null); @Jean-Paul I think this solves your issue
@AnthonyGriggs version 5 support this ... modelBuilder.Entity<ApplicationUser>().ToTable("ApplicationUsers", t => t.ExcludeFromMigrations());
|
89

Building on the other answers I've written this helper that accomplishes the task, including example usage:

public static class Helper{    public static List<T> RawSqlQuery<T>(string query, Func<DbDataReader, T> map)    {        using (var context = new DbContext())        {            using (var command = context.Database.GetDbConnection().CreateCommand())            {                command.CommandText = query;                command.CommandType = CommandType.Text;                context.Database.OpenConnection();                using (var result = command.ExecuteReader())                {                    var entities = new List<T>();                    while (result.Read())                    {                        entities.Add(map(result));                    }                    return entities;                }            }        }    }

Usage:

public class TopUser{    public string Name { get; set; }    public int Count { get; set; }}var result = Helper.RawSqlQuery(    "SELECT TOP 10 Name, COUNT(*) FROM Users U"    + " INNER JOIN Signups S ON U.UserId = S.UserId"    + " GROUP BY U.Name ORDER BY COUNT(*) DESC",    x => new TopUser { Name = (string)x[0], Count = (int)x[1] });result.ForEach(x => Console.WriteLine($"{x.Name,-25}{x.Count}"));

I plan to get rid of it as soon as built-in support is added. According to astatement by Arthur Vickers from the EF Core team it is a high priority for post 2.0. The issue is being trackedhere.

answeredSep 2, 2017 at 11:27
pius's user avatar

8 Comments

Can you also add parameters in a safe way?
Is your helper obsolete since HasNoKey/ Keyless entity types are available since EF core 3.1learn.microsoft.com/en-us/ef/core/modeling/…?
would be great if you could update your post if there is a built in method for this now
you should be disposing both the command and the connection... they both implement IDisposable
made this into DBContext extension, so removed the using on db context, and added DbParameter array at the end, this is great!
|
41

In EF Core you no longer can execute "free" raw sql. You are required to define a POCO class and aDbSet for that class.In your case you will need to defineRank:

var ranks = DbContext.Ranks   .FromSql("SQL_SCRIPT OR STORED_PROCEDURE @p0,@p1,...etc", parameters)   .AsNoTracking().ToList();

As it will be surely readonly it will be useful to include the.AsNoTracking() call.

EDIT - Breaking change in EF Core 3.0:

DbQuery() is now obsolete, insteadDbSet() should be used (again). If you have a keyless entity, i.e. it don't require primary key, you can useHasNoKey() method:

ModelBuilder.Entity<SomeModel>().HasNoKey()

More information can be foundhere

answeredFeb 25, 2016 at 16:58
E-Bat's user avatar

4 Comments

So I guess I will also have to extend theDbContext to include a new propertyDbSet<Rank> Rank { get; set; }. What implications will this have now in reference to linq? I.e. Wont we now be able to use a statement likeDBContext.Rank.Where(i => i.key == 1), and won't this statement have no implementation in SQL and therefore fail?
Linq emitted against this set have to be resolved in memory. If you need to emit different WHERE sql clause you have to include them as parameters or build a different script.
My DbSet does not have a "FromSql" method. Is this an extension I am missing?
@birwin, you need to import namespace Microsoft.EntityFrameworkCore
33

For now, until there is something new from EFCore I would used a commandand map it manually

  using (var command = this.DbContext.Database.GetDbConnection().CreateCommand())  {      command.CommandText = "SELECT ... WHERE ...> @p1)";      command.CommandType = CommandType.Text;      var parameter = new SqlParameter("@p1",...);      command.Parameters.Add(parameter);      this.DbContext.Database.OpenConnection();      using (var result = command.ExecuteReader())      {         while (result.Read())         {            .... // Map to your entity         }      }  }

Try to SqlParameter to avoid Sql Injection.

 dbData.Product.FromSql("SQL SCRIPT");

FromSql doesn't work with full query. Example if you want to include a WHERE clause it will be ignored.

Some Links:

Executing Raw SQL Queries using Entity Framework Core

Raw SQL Queries

Bohdan Stupak's user avatar
Bohdan Stupak
1,59112 silver badges16 bronze badges
answeredJul 23, 2017 at 6:22
Henry's user avatar

Comments

30

You can use this:

    public static class SqlQueryExtensions    {        public static IList<T> SqlQuery<T>(this DbContext db, string sql, params object[] parameters) where T : class        {            using (var db2 = new ContextForQueryType<T>(db.Database.GetDbConnection()))            {                // share the current database transaction, if one exists                var transaction = db.Database.CurrentTransaction;                if (transaction != null)                    db2.Database.UseTransaction(transaction.GetDbTransaction());                return db2.Set<T>().FromSqlRaw(sql, parameters).ToList();            }        }        public static IList<T> SqlQuery<T>(this DbContext db, Func<T> ignored, string sql, params object[] parameters) where T : class            => SqlQuery<T>(db, sql, parameters);        private class ContextForQueryType<T> : DbContext where T : class        {            private readonly DbConnection connection;            public ContextForQueryType(DbConnection connection)            {                this.connection = connection;            }            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)            {                optionsBuilder.UseSqlServer(connection, options => options.EnableRetryOnFailure());                base.OnConfiguring(optionsBuilder);            }            protected override void OnModelCreating(ModelBuilder modelBuilder)            {                modelBuilder.Entity<T>().HasNoKey();                base.OnModelCreating(modelBuilder);            }        }    }

And the usage:

using (var db = new Db()){    var results = db.SqlQuery<ArbitraryType>("select 1 id, 'joe' name");    //or with an anonymous type like this    var results2 = db.SqlQuery(() => new { id =1, name=""},"select 1 id, 'joe' name");}
Jeremy Lakeman's user avatar
Jeremy Lakeman
11.6k30 silver badges34 bronze badges
answeredJan 10, 2019 at 9:22
ErikEJ's user avatar

6 Comments

Anonymous querydb.SqlQuery(() => new { id =1, name=""},"select 1 id, 'joe' name"); throws compile errorType arguments for method SqlQuery<T> cannot inferred from usage. How to use it with anonymuous type. Also Query does not exist in .NET 5. github link contains fixed code
I believe the update @ErikEJ is referring to is found here:github.com/dotnet/efcore/issues/1862#issuecomment-597022290 Brilliant by the way, exactly what I was looking for, thanks! I tried the suggested answer but as of May of 2021 it still tries to create the model. Don't know why the EF team removed db.Database.SqlQuery() without a comparable replacement is beyond me!
@AnthonyGriggs, I don't think EF Core and .NET Core are ready to use from what I am picking up. The more I dig into it the more there's nearly everything missing! They should've just continued maintaining .NET and EF if they knew they had no way of reproducing the good old .NET. Most functions/methods are missing everywhere with so many excuses to why they're missing!
Improved the answer to work inside a db transaction, and defined the extension method used for anonymous types in the example usage.
@JeremyLakeman I'm confused, but I don't see any extension method that would support anonymous types?
|
25

You can execute raw sql in EF Core - Add this class to your project.This will allow you to execute raw SQL and get the raw results without having to define a POCO and a DBSet.Seehttps://github.com/aspnet/EntityFramework/issues/1862#issuecomment-220787464 for original example.

using Microsoft.EntityFrameworkCore.Infrastructure;using Microsoft.EntityFrameworkCore.Internal;using Microsoft.EntityFrameworkCore.Storage;using System.Threading;using System.Threading.Tasks;namespace Microsoft.EntityFrameworkCore{    public static class RDFacadeExtensions    {        public static RelationalDataReader ExecuteSqlQuery(this DatabaseFacade databaseFacade, string sql, params object[] parameters)        {            var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();            using (concurrencyDetector.EnterCriticalSection())            {                var rawSqlCommand = databaseFacade                    .GetService<IRawSqlCommandBuilder>()                    .Build(sql, parameters);                return rawSqlCommand                    .RelationalCommand                    .ExecuteReader(                        databaseFacade.GetService<IRelationalConnection>(),                        parameterValues: rawSqlCommand.ParameterValues);            }        }        public static async Task<RelationalDataReader> ExecuteSqlQueryAsync(this DatabaseFacade databaseFacade,                                                              string sql,                                                              CancellationToken cancellationToken = default(CancellationToken),                                                             params object[] parameters)        {            var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();            using (concurrencyDetector.EnterCriticalSection())            {                var rawSqlCommand = databaseFacade                    .GetService<IRawSqlCommandBuilder>()                    .Build(sql, parameters);                return await rawSqlCommand                    .RelationalCommand                    .ExecuteReaderAsync(                        databaseFacade.GetService<IRelationalConnection>(),                        parameterValues: rawSqlCommand.ParameterValues,                        cancellationToken: cancellationToken);            }        }    }}

Here's an example of how to use it:

// Execute a query.using(var dr = await db.Database.ExecuteSqlQueryAsync("SELECT ID, Credits, LoginDate FROM SamplePlayer WHERE " +                                                          "Name IN ('Electro', 'Nitro')")){    // Output rows.    var reader = dr.DbDataReader;    while (reader.Read())    {        Console.Write("{0}\t{1}\t{2} \n", reader[0], reader[1], reader[2]);    }}
answeredMar 21, 2017 at 16:22
Yehuda Goldenberg's user avatar

Comments

9

try this: (create extension method)

public static List<T> ExecuteQuery<T>(this dbContext db, string query) where T : class, new()        {            using (var command = db.Database.GetDbConnection().CreateCommand())            {                command.CommandText = query;                command.CommandType = CommandType.Text;                db.Database.OpenConnection();                using (var reader = command.ExecuteReader())                {                    var lst = new List<T>();                    var lstColumns = new T().GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();                    while (reader.Read())                    {                        var newObject = new T();                        for (var i = 0; i < reader.FieldCount; i++)                        {                            var name = reader.GetName(i);                            PropertyInfo prop = lstColumns.FirstOrDefault(a => a.Name.ToLower().Equals(name.ToLower()));                            if (prop == null)                            {                                continue;                            }                            var val = reader.IsDBNull(i) ? null : reader[i];                            prop.SetValue(newObject, val, null);                        }                        lst.Add(newObject);                    }                    return lst;                }            }        }

Usage:

var db = new dbContext();string query = @"select ID , Name from People where ... ";var lst = db.ExecuteQuery<PeopleView>(query);

my model: (not inDbSet):

public class PeopleView{    public int ID { get; set; }    public string Name { get; set; }}

tested in.netCore 2.2 and 3.0.

Note: this solution has the slow performance

answeredNov 10, 2019 at 9:24
AminRostami's user avatar

2 Comments

Try search PropertyInfo by name only once for a first record only and build array of PropertyInfo[] by column indexes for using on next records.
@AminRostami Nice Work
8

Add Nuget package - Microsoft.EntityFrameworkCore.Relational

using Microsoft.EntityFrameworkCore;...await YourContext.Database.ExecuteSqlCommandAsync("... @p0, @p1", param1, param2 ..)

This will return the row numbers as an int

See -https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.executesqlcommand?view=efcore-3.0

LuvForAirplanes's user avatar
LuvForAirplanes
7918 silver badges25 bronze badges
answeredDec 24, 2018 at 11:21
Mohsin's user avatar

4 Comments

Please note that this will only return the number of rows affected by the command:stackoverflow.com/a/49861799/299756
Exactly what I need. I am using Microsoft.EntityFrameworkCore 3.1.1 and no way to execute RAW query and SP. Thank you very much for this!
This is what I needed, running .NET 6 & .NET 7 preview.
This is obsolete as of at least EF Core 6.0.
7

In Core 2.1 you can do something like this:

protected override void OnModelCreating(ModelBuilder modelBuilder){       modelBuilder.Query<Ranks>();}

and then define you SQL Procedure, like:

public async Task<List<Ranks>> GetRanks(string value1, Nullable<decimal> value2){    SqlParameter value1Input = new SqlParameter("@Param1", value1?? (object)DBNull.Value);    SqlParameter value2Input = new SqlParameter("@Param2", value2?? (object)DBNull.Value);    List<Ranks> getRanks = await this.Query<Ranks>().FromSql("STORED_PROCEDURE @Param1, @Param2", value1Input, value2Input).ToListAsync();    return getRanks;}

This way Ranks model will not be created in your DB.

Now in your controller/action you can call:

List<Ranks> gettingRanks = _DbContext.GetRanks(value1,value2).Result.ToListAsync();

This way you can call Raw SQL Procedures.

answeredJun 19, 2018 at 10:22
RodrigoCampos's user avatar

1 Comment

TheFromSql params could be simply passed without creatingSqlParameter object:FromSql($"STORED_PROCEDURE {value1}, {value2}") orFromSql("STORED_PROCEDURE {0}, {1}", value1, value2) (they will be escaped).
7

I found the packageEntityFrameworkCore.RawSQLExtensions on github. To use it, add the nuget package.

<PackageReference Include="EntityFrameworkCore.RawSQLExtensions" Version="1.2.0" />

The library is not documented but below is my using of it with .NET 6 + EF Core 6 + Npgsql 6

public class DbResult{    public string Name { get; set; }    public int Age { get; set; }}
using EntityFrameworkCore.RawSQLExtensions.Extensions;
var results = await context.Database    .SqlQuery<DbResult>(        @"select name, age from ""users"" where age > @Age",        new NpgsqlParameter("@Age", 15))    .ToListAsync();
answeredNov 14, 2022 at 12:06
Belyansky Ilya's user avatar

Comments

5

I usedDapper to bypass this constraint of Entity framework Core.

IDbConnection.Query

is working with either sql query or stored procedure with multiple parameters.By the way it's a bit faster (seebenchmark tests )

Dapper is easy to learn. It took 15 minutes to write and run stored procedure with parameters. Anyway you may use both EF and Dapper. Below is an example:

 public class PodborsByParametersService{    string _connectionString = null;    public PodborsByParametersService(string connStr)    {        this._connectionString = connStr;    }    public IList<TyreSearchResult> GetTyres(TyresPodborView pb,bool isPartner,string partnerId ,int pointId)    {        string sqltext  "spGetTyresPartnerToClient";        var p = new DynamicParameters();        p.Add("@PartnerID", partnerId);        p.Add("@PartnerPointID", pointId);        using (IDbConnection db = new SqlConnection(_connectionString))        {            return db.Query<TyreSearchResult>(sqltext, p,null,true,null,CommandType.StoredProcedure).ToList();        }        }}
answeredDec 4, 2018 at 9:48
Lapenkov Vladimir's user avatar

Comments

3

My case used stored procedure instead of raw SQL

Created a class

Public class School{    [Key]    public Guid SchoolId { get; set; }    public string Name { get; set; }    public string Branch { get; set; }    public int NumberOfStudents  { get; set; }}

Added below on myDbContext class

public DbSet<School> SP_Schools { get; set; }

To execute the stored procedure:

var MySchools = _db.SP_Schools.FromSqlRaw("GetSchools @schoolId, @page, @size ",              new SqlParameter("schoolId", schoolId),              new SqlParameter("page", page),              new SqlParameter("size", size))).IgnoreQueryFilters();
marc_s's user avatar
marc_s
760k185 gold badges1.4k silver badges1.5k bronze badges
answeredFeb 22, 2020 at 15:54
NoloMokgosi's user avatar

Comments

3

I updated extension method from @AminRostami to return IAsyncEnumerable (so LINQ filtering can be applied) and it's mapping Model Column name of records returned from DB to models (Tested with EF Core 5):

Extension itself:

public static class QueryHelper{    private static string GetColumnName(this MemberInfo info)    {        List<ColumnAttribute> list = info.GetCustomAttributes<ColumnAttribute>().ToList();        return list.Count > 0 ? list.Single().Name : info.Name;    }    /// <summary>    /// Executes raw query with parameters and maps returned values to column property names of Model provided.    /// Not all properties are required to be present in model (if not present - null)    /// </summary>    public static async IAsyncEnumerable<T> ExecuteQuery<T>(        [NotNull] this DbContext db,        [NotNull] string query,        [NotNull] params SqlParameter[] parameters)        where T : class, new()    {        await using DbCommand command = db.Database.GetDbConnection().CreateCommand();        command.CommandText = query;        command.CommandType = CommandType.Text;        if (parameters != null)        {            foreach (SqlParameter parameter in parameters)            {                command.Parameters.Add(parameter);            }        }        await db.Database.OpenConnectionAsync();        await using DbDataReader reader = await command.ExecuteReaderAsync();        List<PropertyInfo> lstColumns = new T().GetType()            .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();        while (await reader.ReadAsync())        {            T newObject = new();            for (int i = 0; i < reader.FieldCount; i++)            {                string name = reader.GetName(i);                PropertyInfo prop = lstColumns.FirstOrDefault(a => a.GetColumnName().Equals(name));                if (prop == null)                {                    continue;                }                object val = await reader.IsDBNullAsync(i) ? null : reader[i];                prop.SetValue(newObject, val, null);            }            yield return newObject;        }    }}

Model used (note that Column names are different than actual property names):

public class School{    [Key] [Column("SCHOOL_ID")] public int SchoolId { get; set; }    [Column("CLOSE_DATE", TypeName = "datetime")]    public DateTime? CloseDate { get; set; }    [Column("SCHOOL_ACTIVE")] public bool? SchoolActive { get; set; }}

Actual usage:

public async Task<School> ActivateSchool(int schoolId){    // note that we're intentionally not returning "SCHOOL_ACTIVE" with select statement    // this might be because of certain IF condition where we return some other data    return await _context.ExecuteQuery<School>(        "UPDATE SCHOOL SET SCHOOL_ACTIVE = 1 WHERE SCHOOL_ID = @SchoolId; SELECT SCHOOL_ID, CLOSE_DATE FROM SCHOOL",        new SqlParameter("@SchoolId", schoolId)    ).SingleAsync();}
answeredFeb 8, 2021 at 10:17
Jernej Habjan's user avatar

4 Comments

I have to say that this is the most useful answer. Nevertheless, it is not as simple as it looks and there are many edge case issues (e.g. handling nullable types, transactions, string, DateTime, Guid, decimal types, ...). I have code (based on the code above) which deals with all that and I will post it here when I have some free time.
This is it! No DbSet required, prevents SQL injection, automatically maps to target class (T). Thanks alot.
@KonstantinKonstantinov would you mind to post your update please?
@SeriousM - I've just posted the code.
3

Done this for Entity Framework Core 5, need to install

Microsoft.EntityFrameworkCore.Relational

The helper extension methods

using Microsoft.EntityFrameworkCore;using Microsoft.EntityFrameworkCore.Infrastructure;using Microsoft.EntityFrameworkCore.Storage;using System;using System.Collections.Generic;using System.Data;using System.Data.Common;using System.Linq;using System.Linq.Expressions;using System.Reflection;public static class EfHelper{    public static DbTransaction GetDbTransaction(this IDbContextTransaction source)    {        return (source as IInfrastructure<DbTransaction>).Instance;    }    private class PropertyMapp    {        public string Name { get; set; }        public Type Type { get; set; }        public bool IsSame(PropertyMapp mapp)        {            if (mapp == null)            {                return false;            }            bool same = mapp.Name == Name && mapp.Type == Type;            return same;        }    }    public static IEnumerable<T> FromSqlQuery<T>(this DbContext context, string query, params object[] parameters) where T : new()    {        const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;        List<PropertyMapp> entityFields = (from PropertyInfo aProp in typeof(T).GetProperties(flags)                                           select new PropertyMapp                                           {                                               Name = aProp.Name,                                               Type = Nullable.GetUnderlyingType(aProp.PropertyType) ?? aProp.PropertyType                                           }).ToList();        List<PropertyMapp> dbDataReaderFields = new List<PropertyMapp>();        List<PropertyMapp> commonFields = null;        using (var command = context.Database.GetDbConnection().CreateCommand())        {            if (command.Connection.State != ConnectionState.Open)            {                command.Connection.Open();            }            var currentTransaction = context.Database.CurrentTransaction;            if (currentTransaction != null)            {                command.Transaction = currentTransaction.GetDbTransaction();            }            command.CommandText = query;            if (parameters.Any())            {                command.Parameters.AddRange(parameters);            }            using (var result = command.ExecuteReader())            {                while (result.Read())                {                    if (commonFields == null)                    {                        for (int i = 0; i < result.FieldCount; i++)                        {                            dbDataReaderFields.Add(new PropertyMapp { Name = result.GetName(i), Type = result.GetFieldType(i) });                        }                        commonFields = entityFields.Where(x => dbDataReaderFields.Any(d => d.IsSame(x))).Select(x => x).ToList();                    }                    var entity = new T();                    foreach (var aField in commonFields)                    {                        PropertyInfo propertyInfos = entity.GetType().GetProperty(aField.Name);                        var value = (result[aField.Name] == DBNull.Value) ? null : result[aField.Name]; //if field is nullable                        propertyInfos.SetValue(entity, value, null);                    }                    yield return entity;                }            }        }    }    /*     * https://entityframeworkcore.com/knowledge-base/35631903/raw-sql-query-without-dbset---entity-framework-core     */    public static IEnumerable<T> FromSqlQuery<T>(this DbContext context, string query, Func<DbDataReader, T> map, params object[] parameters)    {        using (var command = context.Database.GetDbConnection().CreateCommand())        {            if (command.Connection.State != ConnectionState.Open)            {                command.Connection.Open();            }            var currentTransaction = context.Database.CurrentTransaction;            if (currentTransaction != null)            {                command.Transaction = currentTransaction.GetDbTransaction();            }            command.CommandText = query;            if (parameters.Any())            {                command.Parameters.AddRange(parameters);            }            using (var result = command.ExecuteReader())            {                while (result.Read())                {                    yield return map(result);                }            }        }    }}

Model

public class UserModel{    public string Name { get; set; }    public string Email { get; set; }    public bool? IsDeleted { get; set; }}

Manual mapping

List<UserModel> usersInDb = Db.FromSqlQuery(    "SELECT Name, Email FROM Users WHERE Name=@paramName",    x => new UserModel     {         Name = (string)x[0],         Email = (string)x[1]     },    new SqlParameter("@paramName", user.Name)).ToList();usersInDb = Db.FromSqlQuery(    "SELECT Name, Email FROM Users WHERE Name=@paramName",    x => new UserModel     {         Name = x["Name"] is DBNull ? "" : (string)x["Name"],         Email = x["Email"] is DBNull ? "" : (string)x["Email"]     },    new SqlParameter("@paramName", user.Name)).ToList();

Auto mapping using reflection

List<UserModel> usersInDb = Db.FromSqlQuery<UserModel>(    "SELECT Name, Email, IsDeleted FROM Users WHERE Name=@paramName",    new SqlParameter("@paramName", user.Name)).ToList();
answeredDec 31, 2021 at 16:01
Dipon Roy's user avatar

Comments

2

Not directly targeting the OP's scenario, but since I have been struggling with this, I'd like to drop these ex. methods that make it easier to execute raw SQL with theDbContext:

public static class DbContextCommandExtensions{  public static async Task<int> ExecuteNonQueryAsync(this DbContext context, string rawSql,    params object[] parameters)  {    var conn = context.Database.GetDbConnection();    using (var command = conn.CreateCommand())    {      command.CommandText = rawSql;      if (parameters != null)        foreach (var p in parameters)          command.Parameters.Add(p);      await conn.OpenAsync();      return await command.ExecuteNonQueryAsync();    }  }  public static async Task<T> ExecuteScalarAsync<T>(this DbContext context, string rawSql,    params object[] parameters)  {    var conn = context.Database.GetDbConnection();    using (var command = conn.CreateCommand())    {      command.CommandText = rawSql;      if (parameters != null)        foreach (var p in parameters)          command.Parameters.Add(p);      await conn.OpenAsync();      return (T)await command.ExecuteScalarAsync();    }  }}
answeredDec 24, 2018 at 22:07
Shimmy Weitzhandler's user avatar

Comments

2

This solution leans heavily on the solution from @pius. I wanted to add the option to support query parameters to help mitigate SQL injection and I also wanted to make it an extension off of the DbContext DatabaseFacade for Entity Framework Core to make it a little more integrated.

First create a new class with the extension:

using Microsoft.EntityFrameworkCore;using Microsoft.EntityFrameworkCore.Infrastructure;using Microsoft.EntityFrameworkCore.Metadata;using System;using System.Collections.Generic;using System.Data;using System.Data.Common;using System.Linq;using System.Threading.Tasks;namespace EF.Extend{    public static class ExecuteSqlExt    {        /// <summary>        /// Execute raw SQL query with query parameters        /// </summary>        /// <typeparam name="T">the return type</typeparam>        /// <param name="db">the database context database, usually _context.Database</param>        /// <param name="query">the query string</param>        /// <param name="map">the map to map the result to the object of type T</param>        /// <param name="queryParameters">the collection of query parameters, if any</param>        /// <returns></returns>        public static List<T> ExecuteSqlRawExt<T, P>(this DatabaseFacade db, string query, Func<DbDataReader, T> map, IEnumerable<P> queryParameters = null)        {            using (var command = db.GetDbConnection().CreateCommand())            {                if((queryParameters?.Any() ?? false))                    command.Parameters.AddRange(queryParameters.ToArray());                command.CommandText = query;                command.CommandType = CommandType.Text;                db.OpenConnection();                using (var result = command.ExecuteReader())                {                    var entities = new List<T>();                    while (result.Read())                    {                        entities.Add(map(result));                    }                    return entities;                }            }                        }    }}

Note in the above that "T" is the type for the return and "P" is the type of your query parameters which will vary based on if you are using MySql, Sql, so on.

Next we will show an example. I'm using the MySql EF Core capability, so we'll see how we can use the generic extension above with this more specific MySql implementation:

//add your using statement for the extension at the top of your Controller//with all your other using statementsusing EF.Extend;//then your your Controller looks something like thisnamespace Car.Api.Controllers{    //Define a quick Car class for the custom return type    //you would want to put this in it's own class file probably    public class Car    {        public string Make { get; set; }        public string Model { get; set; }        public string DisplayTitle { get; set; }    }    [ApiController]    public class CarController : ControllerBase    {        private readonly ILogger<CarController> _logger;        //this would be your Entity Framework Core context        private readonly CarContext _context;        public CarController(ILogger<CarController> logger, CarContext context)        {            _logger = logger;            _context = context;        }        //... more stuff here ...       /// <summary>       /// Get car example       /// </summary>       [HttpGet]       public IEnumerable<Car> Get()       {           //instantiate three query parameters to pass with the query           //note the MySqlParameter type is because I'm using MySql           MySqlParameter p1 = new MySqlParameter           {               ParameterName = "id1",               Value = "25"           };           MySqlParameter p2 = new MySqlParameter           {               ParameterName = "id2",               Value = "26"           };           MySqlParameter p3 = new MySqlParameter           {               ParameterName = "id3",               Value = "27"           };           //add the 3 query parameters to an IEnumerable compatible list object           List<MySqlParameter> queryParameters = new List<MySqlParameter>() { p1, p2, p3 };           //note the extension is now easily accessed off the _context.Database object           //also note for ExecuteSqlRawExt<Car, MySqlParameter>           //Car is my return type "T"           //MySqlParameter is the specific DbParameter type MySqlParameter type "P"           List<Car> result = _context.Database.ExecuteSqlRawExt<Car, MySqlParameter>(        "SELECT Car.Make, Car.Model, CONCAT_WS('', Car.Make, ' ', Car.Model) As DisplayTitle FROM Car WHERE Car.Id IN(@id1, @id2, @id3)",        x => new Car { Make = (string)x[0], Model = (string)x[1], DisplayTitle = (string)x[2] },         queryParameters);           return result;       }    }}

The query would return rows like:
"Ford", "Explorer", "Ford Explorer"
"Tesla", "Model X", "Tesla Model X"

The display title is not defined as a database column, so it wouldn't be part of the EF Car model by default. I like this approach as one of many possible solutions. The other answers on this page reference other ways to address this issue with the [NotMapped] decorator, which depending on your use case could be the more appropriate approach.

Note the code in this example is obviously more verbose than it needs to be, but I thought it made the example clearer.

answeredAug 9, 2020 at 17:42
dan-iel's user avatar

1 Comment

Thanks Dan-iel. one more thing i needed to add package for "Microsoft.EntityFrameworkCore.Relational
1

Actually you can create a generic repository and do something like this

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : BaseEntity{    private readonly DataContext context;    private readonly DbSet<TEntity> dbSet;    public GenericRepository(DataContext context)    {        this.context = context;        this.dbSet = context.Set<TEntity>();    }       public IEnumerable<TEntity> ExecuteCommandQuery(string command)        => dbSet.FromSqlRaw(command);}
answeredSep 1, 2020 at 11:05
Denys's user avatar

3 Comments

addition: Microsoft.EntityFrameworkCore doesn't contain FromSqlRaw. Is is nessesary to install Microsoft.EntityFrameworkCore.Relational to use this method.
It's not possible with efcore5.0 because the TEntity must be registered first."Cannot create a DbSet for 'DatabaseFirewallRuleModel' because this type is not included in the model for the context."
wish I had read @SeriousM comment a couple hours ago. Is there a solution to this in EF Core 6 or 7 ?
1

For Querying Data: Without existing Entity

               string query = "SELECT r.Name as roleName, ur.roleId, u.Id as userId   FROM dbo.AspNetUserRoles AS ur INNER JOIN dbo.AspNetUsers AS u ON ur.UserId = u.Id INNER JOIN dbo.AspNetRoles AS r ON ur.RoleId = r.Id ";               ICollection<object> usersWithRoles = new List<object>();                using (var command = _identityDBContext.Database.GetDbConnection().CreateCommand())                {                    command.CommandText = query;                    command.CommandType = CommandType.Text;                    await _identityDBContext.Database.OpenConnectionAsync();                    using (var reader = await command.ExecuteReaderAsync())                    {                              while (await reader.ReadAsync())                        {                            usersWithRoles.Add(new {                                 roleName = reader.GetFieldValueAsync<string>(0).Result,                                 roleId = reader.GetFieldValueAsync<string>(1).Result,                                userId = reader.GetFieldValueAsync<string>(2).Result                            });                        }                        }                }

Detailed:

 [HttpGet]    [Route("GetAllUsersWithRoles")]    public async Task<IActionResult> GetAllUsersWithRoles()    {        string query = "SELECT r.Name as roleName, ur.roleId, u.Id as userId   FROM dbo.AspNetUserRoles AS ur INNER JOIN dbo.AspNetUsers AS u ON ur.UserId = u.Id INNER JOIN dbo.AspNetRoles AS r ON ur.RoleId = r.Id ";        try        {            ICollection<object> usersWithRoles = new List<object>();            using (var command = _identityDBContext.Database.GetDbConnection().CreateCommand())            {                command.CommandText = query;                command.CommandType = CommandType.Text;                await _identityDBContext.Database.OpenConnectionAsync();                using (var reader = await command.ExecuteReaderAsync())                {                          while (await reader.ReadAsync())                    {                        usersWithRoles.Add(new {                             roleName = reader.GetFieldValueAsync<string>(0).Result,                             roleId = reader.GetFieldValueAsync<string>(1).Result,                            userId = reader.GetFieldValueAsync<string>(2).Result                        });                    }                    }            }                return StatusCode(200, usersWithRoles); // Get all users           }        catch (Exception e)        {            return StatusCode(500, e);        }    }

RESULT looks like this:

[  {    "roleName": "admin",    "roleId": "7c9cb1be-e987-4ec1-ae4d-e4c9790f57d8",    "userId": "12eadc86-6311-4d5e-8be8-df30799df265"  },  {    "roleName": "user",    "roleId": "a0d5ef46-b1e6-4a53-91ce-9ff5959f1ed8",    "userId": "12eadc86-6311-4d5e-8be8-df30799df265"  },  {    "roleName": "user",    "roleId": "a0d5ef46-b1e6-4a53-91ce-9ff5959f1ed8",    "userId": "3e7cd970-8c52-4dd1-847c-f824671ea15d"  }]
answeredOct 29, 2021 at 12:43

Comments

0

You can also useQueryFirst. Like Dapper, this is totally outside EF. Unlike Dapper (or EF), you don't need to maintain the POCO, you edit your sql SQL in a real environment, and it's continually revalidated against the DB. Disclaimer: I'm the author of QueryFirst.

answeredDec 5, 2018 at 10:44
bbsimonbb's user avatar

Comments

0

I've came to this question because we have over 100 instances of entity-less usages ofSqlQuery in Entity Framework 6 and so going the Microsoft suggested way(s) simply cannot not easily work in our case.

In addition, we had to maintain a singleEF (Entity Framework 6) /EFC (Entity Framework Core 5) code base for several months, while migrating fromEF toEFC. The code base is fairly large and it was simply impossible to migrate "overnight".

The answer below is based on great answers above and it is just a small extension to make them work for a few more edge cases.

First, for eachEF based project we created anEFC based project (e.g.MyProject.csproj ==>MyProject_EFC.csproj) and inside all suchEFC projects we defined a constantEFCORE. If you are doing a quick one-time migration fromEF toEFC, then you don't need that and you can just keep what's inside#if EFCORE ... #else and remove what's inside#else ... #endif below.

Here is the main interop extension class.

using System;using System.Collections.Generic;using System.Threading;#if EFCOREusing System.ComponentModel.DataAnnotations.Schema;using System.Data;using System.Data.Common;using System.Linq;using System.Reflection;using System.Threading.Tasks;using Microsoft.EntityFrameworkCore;using Microsoft.EntityFrameworkCore.ChangeTracking;using Microsoft.EntityFrameworkCore.Storage;using Database = Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade;using MoreLinq.Extensions;#elseusing System.Data.Entity;using System.Data.Entity.Infrastructure;#endifnamespace YourNameSpace.EntityFrameworkCore{    /// <summary>    /// Collection of extension methods to simplify migration from EF to EFC.    /// </summary>    public static class EntityFrameworkCoreInterop    {        /// <summary>        /// https://stackoverflow.com/questions/6637679/reflection-get-attribute-name-and-value-on-property        /// </summary>        public static TAttribute? TryGetAttribute<TAttribute>(this PropertyInfo prop) where TAttribute : Attribute =>            prop.GetCustomAttributes(true).TryGetAttribute<TAttribute>();        public static TAttribute? TryGetAttribute<TAttribute>(this Type t) where TAttribute : Attribute =>            t.GetCustomAttributes(true).TryGetAttribute<TAttribute>();        public static TAttribute? TryGetAttribute<TAttribute>(this IEnumerable<object> attrs) where TAttribute : Attribute        {            foreach (object attr in attrs)            {                switch (attr)                {                    case TAttribute t:                    {                        return t;                    }                }            }            return null;        }        /// <summary>        /// Returns true if the source string matches *any* of the passed-in strings (case insensitive)        /// </summary>        public static bool EqualsNoCase(this string? s, params string?[]? targets)        {            if (s == null && (targets == null || targets.Length == 0))            {                return true;            }            if (targets == null)            {                return false;            }            return targets.Any(t => string.Equals(s, t, StringComparison.OrdinalIgnoreCase));        }#if EFCORE        public class EntityException : Exception        {            public EntityException(string message) : base(message)            {            }        }        public static TEntity GetEntity<TEntity>(this EntityEntry<TEntity> entityEntry)            where TEntity : class => entityEntry.Entity;        #region SqlQuery Interop        /// <summary>        /// kk:20210727 - This is a little bit ugly but given that this interop method is used just once,        /// it is not worth spending more time on it.        /// </summary>        public static List<T> ToList<T>(this IOrderedAsyncEnumerable<T> e) =>            Task.Run(() => e.ToListAsync().AsTask()).GetAwaiter().GetResult();        private static string GetColumnName(this MemberInfo info) =>            info.GetCustomAttributes().TryGetAttribute<ColumnAttribute>()?.Name ?? info.Name;        /// <summary>        /// See: https://stackoverflow.com/questions/35631903/raw-sql-query-without-dbset-entity-framework-core        /// Executes raw query with parameters and maps returned values to column property names of Model provided.        /// Not all properties are required to be present in the model. If not present then they will be set to nulls.        /// </summary>        private static async IAsyncEnumerable<T> ExecuteQuery<T>(this Database database, string query, params object[] parameters)        {            await using DbCommand command = database.GetDbConnection().CreateCommand();            command.CommandText = query;            command.CommandType = CommandType.Text;            if (database.CurrentTransaction != null)            {                command.Transaction = database.CurrentTransaction.GetDbTransaction();            }            foreach (var parameter in parameters)            {                // They are supposed to be of SqlParameter type but are passed as objects.                command.Parameters.Add(parameter);            }            await database.OpenConnectionAsync();            await using DbDataReader reader = await command.ExecuteReaderAsync();            var t = typeof(T);            // TODO kk:20210825 - I do know that the code below works as we use it in some other place where it does work.            // However, I am not 100% sure that R# proposed version does. Check and refactor when time permits.            //            // ReSharper disable once CheckForReferenceEqualityInstead.1            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))            {                t = Nullable.GetUnderlyingType(t)!;            }            var lstColumns = t                .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)                .ToList();            while (await reader.ReadAsync())            {                if (t.IsPrimitive || t == typeof(string) || t == typeof(DateTime) || t == typeof(Guid) || t == typeof(decimal))                {                    var val = await reader.IsDBNullAsync(0) ? null : reader[0];                    yield return (T) val!;                }                else                {                    var newObject = Activator.CreateInstance<T>();                    for (var i = 0; i < reader.FieldCount; i++)                    {                        var name = reader.GetName(i);                        var val = await reader.IsDBNullAsync(i) ? null : reader[i];                        var prop = lstColumns.FirstOrDefault(a => a.GetColumnName().EqualsNoCase(name));                        if (prop == null)                        {                            continue;                        }                        prop.SetValue(newObject, val, null);                    }                    yield return newObject;                }            }        }        #endregion        public static DbRawSqlQuery<TElement> SqlQuery<TElement>(this Database database, string sql, params object[] parameters) =>            new(database, sql, parameters);        public class DbRawSqlQuery<TElement> : IAsyncEnumerable<TElement>        {            private readonly IAsyncEnumerable<TElement> _elements;            internal DbRawSqlQuery(Database database, string sql, params object[] parameters) =>                _elements = ExecuteQuery<TElement>(database, sql, parameters);            public IAsyncEnumerator<TElement> GetAsyncEnumerator(CancellationToken cancellationToken = new ()) =>                _elements.GetAsyncEnumerator(cancellationToken);            public async Task<TElement> SingleAsync() => await _elements.SingleAsync();            public TElement Single() => Task.Run(SingleAsync).GetAwaiter().GetResult();            public async Task<TElement> FirstAsync() => await _elements.FirstAsync();            public TElement First() => Task.Run(FirstAsync).GetAwaiter().GetResult();            public async Task<TElement?> SingleOrDefaultAsync() => await _elements.SingleOrDefaultAsync();            public async Task<int> CountAsync() => await _elements.CountAsync();            public async Task<List<TElement>> ToListAsync() => await _elements.ToListAsync();            public List<TElement> ToList() => Task.Run(ToListAsync).GetAwaiter().GetResult();        }#endif    }}

and the usages are indistinguishable from the formerEF usages:

public async Task<List<int>> GetMyResults(){    using var ctx = GetMyDbContext();    const string sql = "select 1 as Result";    return await ctx.GetDatabase().SqlQuery<int>(sql).ToListAsync();}

whereGetMyDbContext is a method to get your database context andGetDatabase is an one-liner interop that returns((DbContext)context).Database for a givenIMyDbContext : DbContext. This is to simplify simultaneousEF /EFC operations.

This works for primitive types (the example is above), entities, local classes (but not anonymous ones). Column renaming is supported viaGetColumnName, but, ... it was already done above.

answeredSep 21, 2021 at 23:42
Konstantin Konstantinov's user avatar

Comments

0

Here is very simple code for SqlQueryRaw, you need to pass your model and get list of model in return

public IList<OrderViewModel> OrderModel { get; set; } = default!;public async Task<List<OrderViewModel>> GetUserOrder(){    var user = await _userManager.GetUserAsync(User);    if (user == null)    {        return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");    }    else    {        var orderViewModel = _db.Database.SqlQueryRaw<OrderViewModel>("Select oi.Id as OrderItemId,o.Id as OrderId,p.Id as ProductId,ProductName,c.CategoryName,p.Price, " +            "Image as ProductImage,oi.Quantity,o.UserId,o.AddedDate as OrderDate\r\nFrom OrderItems oi inner join Orders o on oi.orderid=o.id inner join " +            "Products p on p.Id=oi.ProductId inner join Categories c on c.Id=p.CategoryId Where O.UserId='"+user.Id+"'").AsNoTracking().ToList();        OrderModel = orderViewModel;    }    return OrderModel;}
answeredJun 12, 2024 at 10:35
Sanjay Dwivedi's user avatar

Comments

0

in ef core 8.0, when i want to mapping a sql view to entity, doing like this :

sql view


SELECT TOP (1000) [Person],[age] FROM [PersonSamDb].[dbo].[vw_test]

mapping entity


[NotMapped]public class PersonView{public string Person { get; set; }public int age { get; set; }}

DbContext


 public class PersonDbContext:DbContext {public DbSet<PersonView> PersonViews { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder){     modelBuilder.Entity<PersonView>().HasNoKey();     modelBuilder.Entity<PersonView>().ToView("vw_test"); } }

after that, you can add new migration and update database then use like other entitiesgood luck!

answeredSep 10, 2024 at 9:22
tohid badri's user avatar

Comments

-8

With Entity Framework 6 you can execute something like below

Create Modal Class as

Public class User{        public int Id { get; set; }        public string fname { get; set; }        public string lname { get; set; }        public string username { get; set; }}

Execute Raw DQL SQl command as below:

var userList = datacontext.Database.SqlQuery<User>(@"SELECT u.Id ,fname , lname ,username FROM dbo.Users").ToList<User>();
answeredJul 10, 2019 at 18:54
Siddhartha's user avatar

Comments

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.