I'm using CodeFirst and the repository pattern for my program. The database I'm using as my context has a stored procedure I created and tested in SQL Server Management Studio called dbo.sp_InsertTrackingInfo.
In my base repository class I have
public IEnumerable<T> ExecWithStoreProcedure(string query, int id)
{
return _context.Database.SqlQuery<T>("sp_InsertTrackingInfo #estimate", new SqlParameter("estimate", id));
}
Using this query in the management studio works, where estimate is defined as an integer
EXEC sp_InsertTrackingInfo #estimate = '14'
All operations made work with the exception of this so I know I'm pointing to the right place, I've checked the SQL Server Profiler and no calls to this procedure were made at any point this function was called.
Anybody have experience with this?
Thanks,
Bmckie
EDIT:
In one of the repositories
using (var uow = UnitOfWorkManager.Begin())
{
uow.EstimateTrackingRepository.Insert(t);
uow.EstimateTrackingRepository.ExecWithStoreProcedure("exec sp_InsertTrackingInfo #estimate", t.EstimateId);
uow.Commit();
}
If the procedure just does an insert statement, you can use ExecuteSqlCommand.
int rowsAffected = _context.Database
.ExecuteSqlCommand("sp_InsertTrackingInfo {0}", id);
If it does a select statement, you need to execute the query, because it's a deferred (delayed) execution until something needs it.
var items = _context.Database
.SqlQuery<T>("sp_InsertTrackingInfo {0}", id)
.ToArray(); // or FirstOrDefault() or Any() etc
Related
I have the below block of code:
private TData ExecuteReturnData<TData>(string procName, Func<IDataReader, TData> translator, SqlConnection sqlCon, params SqlParameter[] parameters)
{
using var sqlCmd = CreateCommand(procName, sqlCon, parameters);
sqlCmd.CommandTimeout=120;
using var reader = sqlCmd.ExecuteReader();
var elems = translator(reader);
return elems
}
Here I want to see the complete SQL command execution with params in SQL Server code block. How can I check that here?
What you're asking for does not exist. It never exists at any point. The entire purpose of using parameterized queries is the parameter data is NEVER substituted directly into the SQL command string, and therefore will not be available to view in that way.
Parameterized queries are more than simply sanitizing or escaping in the parameter data in the proper way; they quarantine the data from the command, so the two can never meet.
That is, if you have this query:
SELECT * FROM Users WHERE FirstName= #FirstName
and this parameter value:
Samuel
instead of something like this:
SELECT * FROM Users WHERE FirstName = 'Samuel'
The parameter data is sent to the server in a completely separate block than the SQL command. The server receives both parts and does something more like this:
DECLARE #FirstName nvarchar(40) = LoadParameterFromClient()
SELECT * FROM Users WHERE FirstName= #FirstName
(Note: the actual mechanism for this is sp_executesql)
But for what it's worth, I tend to structure similar C# code more like this:
private IEnumerable<TData> ExecuteReturnData<TData>(string SQL, Func<IDataRecord, TData> translator, Action<SqlParameterCollection> addParams)
{
using var conn = new SqlConnection(" ... "); // My data layer knows about the database I'm using, so I don't need to pass in a conneciton
using var cmd = new SqlCommand(SQL, conn);
if (addParams is object) addParams(cmd.Parameters);
conn.Open();
using var reader = sqlCmd.ExecuteReader();
while (reader.Read())
{
yield return translator(reader);
}
}
Then I'd call it using a similar example as above like this:
var results = ExecuteReturnData<string>("SELECT FirstName, LastName FROM Users WHERE FirstName=#FirstName",
p => p.Add("#FirstName", SqlDbType.NVarchar,40).Value = "Samuel",
r => r["LastName"] + ", " + r["FirstName"]);
For more complex result types I'd have a static FromSQL(IDataRecord data) method on the target type, to avoid making this function call too difficult to read:
var results = ExecuteReturnData<User>("SELECT * FROM Users WHERE FirstName=#FirstName",
p => p.Add("#FirstName", SqlDbType.NVarchar,40).Value = "Samuel",
User.FromSQL);
As a project grows I might also collect these methods into a separate static type, to avoid over-coupling between the data layer and client code.
And of course you can run stored procedures the same way:
var results = ExecuteReturnData("exec MyProcedure #Param1, #Param2" ... );
How to see SQL command execution with Stored Procedure
I want to see the complete SQL command execution with params in SQL Server code block. How can I check that here?
In Visual Studio open the SQL Server Object Explorer > New Connection to SQL Server > Expand Database > Expand Programmatibility > Stored Procedures and Right Click on a Sproc and choose Debug Procedure. You can step though the T-SQL Code. But you can't jump into the T-SQL from the .Net Code which sounds like what you want.
Debug Procedure > Press F11 to Step into the Stored Procedure:
REF: https://learn.microsoft.com/en-us/sql/ssms/scripting/transact-sql-debugger?view=sql-server-ver16
This question already has answers here:
Why Entity Framework performs faster than Dapper in direct select statement [closed]
(4 answers)
Closed 7 months ago.
I read that Dapper is faster than EF (at least at retrieving data) and I want to confirm that so I am comparing Dapper and EntityFramework with the help of BenchmarkDotNet.
So I tried this...
[Benchmark]
public Player EntityFramework_GetByName()
{
using (ApplicationDbContext context = new())
{
return context.Players.FirstOrDefault(x => x.FirstName == _name);
}
}
[Benchmark]
public Player Dapper_GetByName()
{
using (SqlConnection conn = new(Database.ConnectionString))
{
return conn.QueryFirstOrDefault<Player>($"SELECT * FROM Players WHERE FirstName = '{_name}'");
}
}
But the result are not what I expecting...
Then I read here about the column type "problem" and how that can affect the performance, so I change the type of the column to NVarchar with max length of 100 and my code for the Dapper to this
[Benchmark]
public Player Dapper_GetByName()
{
using (SqlConnection conn = new(Database.ConnectionString))
{
return conn.QueryFirstOrDefault<Player>($"SELECT * FROM Players WHERE FirstName = #name", new
{ #name = new DbString { Value = _name, IsAnsi = false } });
}
}
The results of the benchmark tests are the following..
Method
Mean
Error
StdDev
Allocated
Dapper_GetByName
41,092.8 us
1,400.39 us
4,085.0 us
4 KB
EntityFramework_GetByName
2,971.6 us
305.43 us
895.8 us
110 KB
The difference is very big. Is there a way to improve this?
Uhm, maybe you should not compare
// Open and Close a completely new database connection
using (SqlConnection conn = new(Database.ConnectionString))
vs
// Create a new Unit of Work / Transaction
using (ApplicationDbContext context = new())
Benchmark only the inner part:
return conn.QueryFirstOrDefault<Player>($"SELECT * FROM Players WHERE FirstName = '{_name}'");
I think this example shows very clearly the responsibility of SQL query generation when using Dapper, CA.Blocks.DataAccess or ADO.NET directly. When using these packages for accessing the database the developer is entirely in charge of the SQL query, its projection and execution. When using EF the responsibility of generating the query is removed from the developer and delegated to EF. This is a double-edged sword and can result in good queries as well as very bad queries. Most of the performance gains made in Dapper are from having full control over the SQL and eliminating bad SQL generation. The converse is also true, most of the performance problems with Dapper when compared to EF are due to EF creating a better query.
So what is happening here. In simple terms EF has looked at the request and has knowledge that you only what the first record FirstOrDefault so its query generation has resulted in
SELECT TOP 1 * FROM … WHERE…
The Dapper query you are making the comparison with is
SELECT * FROM … WHERE …
So the difference I suspect is purely on SQL. The Test database used, probably has many records in the Person table. Given the numbers it is likely that there is no index on name resulting in a Table Scan to find the matching data.
In the query generated by EF the database can stop the execute as soon as it finds the first record, in the Dapper example the database assembles the full record set with all the matches based on name then sends that row-set. Dapper is simply reading the first row and closing the connection.
To make this a fair comparison you need to change the query to be top 1. Like
[Benchmark]
public Player Dapper_GetByName()
{
using (SqlConnection conn = new(Database.ConnectionString))
{
return conn.QueryFirstOrDefault<Player>($"SELECT Top 1 * FROM Players WHERE FirstName = #name", new
{ #name = new DbString { Value = _name, IsAnsi = false } });
}
}
Also, the decision to go with Dapper for performance means you need to get to know and love SQL.
I am working on the security and user management of a new platform built entirely in .NET Core.
In particular I am trying to generate a random password for new users. I have loaded a large list of English words into a table and created a stored procedure to select random words from the table and compose a password in the correct-horse-battery-staple format.
The stored procedure (Passwords.GenerateRandomPassword) takes no parameters and returns a single line varchar column named password.
Everything works up to this point. I can run the query directly against the server and it works fine.
I have a method on my userRepository like so:
public async Task<string> GenerateRandomPassword()
{
}
but I cannot figure out how to get EF Core 3.14 to call this stored procedure and return a value.
Documentation may not be up to date, or maybe I'm missing an assembly reference.
The context object and the context.database object do not seem to contain any methods that look like they will allow me to execute a stored procedure and retrieve a value.
Documentation seems to suggest that there should be a FromSQL method or similar.
Any help will be greatly appreciated.
The general solution is to call db.Database.GetDbConnection(), which gives you the ADO.NET connection object that you can use directly.
eg
var con = (SqlConnection)db.Database.GetDbConnection();
con.Open();
var cmd = con.CreateCommand();
cmd.CommandText = "exec ...";
There's also the db.Database.ExecuteSqlxxx methods, which work for simple cases.
What you want to look at is keyless entity types.
This is a new feature in EF Core 3.0.
One usage is to retrieve data from raw sql queries.
Using PostgreSQL.
In PostgreSQL we create a function to generate a password:
CREATE OR REPLACE FUNCTION generate_password() RETURNS text AS $$
BEGIN
RETURN (SELECT substring(md5(random()::text) from 0 for 12));
END
$$ LANGUAGE plpgsql;
We create our entity:
public class PasswordGenerator
{
public string Password { get; set; }
}
In our application's DbContext we configure our entity:
public DbSet<PasswordGenerator> PasswordGenerator { get; set; }
public MyDbContext(DbContextOptions options)
: base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PasswordGenerator>().HasNoKey();
}
We use the FromSqlRaw method to call our function that returns our password:
public async Task<string> GetGeneratedPassword()
{
var pg = await _context.PasswordGenerator
.FromSqlRaw(#"SELECT * from generate_password() AS ""Password""")
.FirstAsync();
return pg.Password;
}
We use the alias "Password" to correctly map our query to our entity.
The two packages installed are: Microsoft.EntityFrameworkCore and Npgsql.EntityFrameworkCore.PostgreSQL.
Using SQL Server.
We create a stored procedure to generate a password:
CREATE OR ALTER PROCEDURE generate_password
AS
SET NOCOUNT ON
SELECT SUBSTRING (CONVERT(varchar(255), NEWID()), 0, 12) AS "Password"
RETURN
GO
Use the alias "Password" to correctly map our query to our entity.
Here's how we use the FromSqlRaw method:
public async Task<string> GetGeneratedPassword()
{
var pg = (await _context.PasswordGenerator
.FromSqlRaw("EXEC generate_password")
.ToListAsync())
.First();
return pg.Password;
}
LINQ queries expect our raw queries to be composable, which is why we call ToListAsync() right after the FromSqlRaw method.
SQL Server doesn't allow composing over stored procedure calls, so any
attempt to apply additional query operators to such a call will result
in invalid SQL. Use AsEnumerable or AsAsyncEnumerable method right
after FromSqlRaw or FromSqlInterpolated methods to make sure that EF
Core doesn't try to compose over a stored procedure.
The two packages installed are: Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer
UPDATE - Using the GetDbConnection method
Thanks to the answer provided by #David Browne - Microsoft, we can call the GetDbConnection extension method to access the database directly.
public async Task<string> GetGeneratedPassword()
{
var password = "";
var connection = _context.Database.GetDbConnection();
try
{
await connection.OpenAsync();
using (var command = connection.CreateCommand())
{
// SQL Server
command.CommandText = "EXEC generate_password";
// PostgreSQL
// command.CommandText = #"SELECT * from generate_password()";
using (var reader = await command.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
password = reader.GetString(0);
}
}
}
}
finally
{
await connection.CloseAsync();
}
return password;
}
Unfortunately my EF application has to call stored procedures I am unable to change. While this is not ideal I can normally get around it. However, I have a stored proc that does return anything. How does EF core deal with this? I know in previous versions you could run ExecuteNonQuery but I haven't been able to find anything similar in EF Core.
I normally run my queries through a helper as such, where T is a class that maps to a return type EF can serialize to:
context.Set<T>()
.AsNoTracking()
.FromSql(query, args)
.ToListAsync();
However it looks like Set always requires a type as does .Query. Nothing else I've seen off of context would allow you to make a non-queryable call. Am I missing something?
I am using Microsoft.EntityFrameworkCore: 1.2.0
You can use the DbContext.DatabaseExecuteSqlCommand method
using(var context = new SampleContext())
{
var commandText = "INSERT Categories (CategoryName) VALUES (#CategoryName)";
var name = new SqlParameter("#CategoryName", "Test");
context.Database.ExecuteSqlCommand(commandText, name);
}
Or you can revert to ADO.NET calls off the Context:
using (var context = new SampleContext())
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "DELETE From Table1";
context.Database.OpenConnection();
command.ExecuteNonQuery();
}
I am working on a new project that needs to use Linq To SQL. I have been asked to create a generic or reusable Linq to SQL class that can be used to execute stored procedures.
In ADO.Net I knew how to do this by just passing in a string of what I wanted to execute and I could pass in different strings for each query I need to run:
SqlCommand cmd = new SqlCommand("myStoredProc", conn); // etc, etc
I am struggling with how to create something similar in Linq To SQL, if it is even possible. I have created a .dbml file and added my stored procedure to it. As a result, I can return the results using the code below:
public List<myResultsStoreProc> GetData(string connectName)
{
MyDataContext db = new MyDataContext (GetConnectionString(connectName));
var query = db.myResultsStoreProc();
return query.ToList();
}
The code works but they want me to create one method that will return whatever stored procedure I tell it to run. I have searched online and talked to colleagues about this and have been unsuccessful in finding a way to create reusable stored proc class.
So is there a way to create a reusable Linq to SQL class to execute stored procs?
Edit:
What I am looking for is if there is a way to do something like the following?
public List<string> GetData(string connectName, string procedureName)
{
MyDataContext db = new MyDataContext (GetConnectionString(connectName));
var query = db.procedureName();
return query.ToList();
}
I have reviewed the MSDN docs on Linq To Sql and these are showing the table in the IEnumerable:
IEnumerable<Customer> results = db.ExecuteQuery<Customer>(
#"select c1.custid as CustomerID, c2.custName as ContactName
from customer1 as c1, customer2 as c2
where c1.custid = c2.custid"
);
I am looking for something very generic, where I can send in a string value of the stored proc that I want to execute. If this is not possible, is there any documentation on why it cannot be done this way? I need to prove why we cannot pass a string value of the name of the procedure to execute in Linq To Sql
DataContext.ExecuteCommand is not quite what you are looking for, as it only returns an int value. What you want instead is DataContext.ExecuteQuery, which is capable of executing a stored procedure and returning a dataset.
I would create a partial class for your DBML in which to store this function.
public List<T> GetDataNoParams(string procname)
{
var query = this.ExecuteQuery<T>("Exec " + procname);
return query.ToList();
}
public List<T> GetDataParams(string procname, Object[] parameters)
{
var query = this.ExecuteQuery<T>("Exec " + procname, parameters);
return query.ToList();
}
To call a stored procedure you would do:
GetDataNoParams("myprocedurename");
or
GetDataParams("myotherprocedure {0}, {1}, {2}", DateTime.Now, "sometextValue", 12345);
or
GetDataParams("myotherprocedure var1={0}, var2={1}, var3={2}", DateTime.Now, "sometextValue", 12345);
If you want to call procedures with no return value that is easy enough too, as I'm sure you can see, by creating a new method that doesn't store/return anything.
The inspiration came from http://msdn.microsoft.com/en-us/library/bb361109(v=vs.90).aspx.
The simplest answer to your question is that you can grab the Connection property of your MyDataContext and create and execute your own SqlCommands just like you would in straight up ADO.Net. I'm not sure if that will serve your purposes, especially if you want to retrieve entities from your LINQ to SQL model.
If you want to return entities from the model, then have a look at the DataContext.ExecuteCommand method.
When we drop a Table or StoredProcedure in our .dbml file it creates its class which communicates with the data layer and our business logic.
In Linq to SQL we have to have the StoredProcedures or Tables present in the .dbml file otherwise there is no way to call a generic method in Linq to SQL for calling a stored procedure by passing its name to a method.
But in ADO.Net we can do it (like you know)
SqlCommand cmd = new SqlCommand("myStoredProc", conn);