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);
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
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;
}
Im working with ASP.NET MVC and I have a model of a Stored Procedure that I created using EF Designer from my exisisting Database. Here I put my code:
public class SpGetClientPhonesController : ApiController
{
ShopEntities2 db = new ShopEntities2();
public string Get()
{
var json = "";
spGetRegister_Result objmodel = new spGetRegister_Result( *HERE I SHOULD SPECIFY THE SP INPUT PARAMETERS*);
}
}
I want to use this model to retrieve the data that the store procedure returns but I need to specify the input parameters first that this Store Procedure needs. How can I do that??
If your model has been generated from the database and you have your stored proc referenced in it.
Then the way you would call the stored proc with Entity Framework would be as follows:
using(var db = new ShopEntities2())
{
int id = 1234;
var result = db.spGetRegister(id); //You should have intellisense here for your parameter(s) so `id` may not exactly correct but you get the idea.
}
I was digging and asking a little bit more and I was able to solve this problem. When Stored Procedures are mapped into Models using Entity Framework, you have to work with 2 concepts. Using my prevoius example with spGetRegister, when I created my model I had spGetRegister and spGetRegister_Result.
The spGetRegister_Result is the one who handles what the Stored Procedure returns and spGetRegister is what I have to call with its input parameters. Here is the example:
ObjectResult<spGetRegister_Result> newRecord = new spGetRegister_Result;
newRecord = db.spGetRegister( IdClient:9587, IdSite:94);
From here you have the resulting row(s) of the Stored Procedure on the variable newRecord and you can do whatever you want with it.
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
I've created my stored procedure, I've created the complex type using the entity model. Now assuming I have successfully established a connection to the database - I'm now ready to run the store procedure and store the rows in a List<ComplexType>. How do I do this in the best, most efficient way? I'm aware that I can iterate through the columns and rows of an SQLDataReader but it sort of feels like I'd be missing the point of the entity framework.
Thanks a lot.
You can add the stored procedure as a function import and then call it in your assembly directly:
Using (var context = new NorthwindEntities())
{
Var query = context.GetEmployeeNames(); // we import the stored procedure as a function GetEmployeeNames().
//…
}
If you use OUTPUT parameter in your stored procedure, you need to add ObjectParameters to get the return value. For example,
Using(var context = new NorthwindEntities())
{
ObjectParameter firstname = new ObjectParameter(“firstname”, typeof(String));
ObjectParameter lastname = new ObjectParameter(“lastname”, typeof(String));
Var query = context.GetEmployeeByID(123, firstname, lastname);
// Console.WriteLine(“Employee {0}’s name is: {1}.{2}.”, 123, firstname, lastname);
}
Here is a live sample:
ReferralVisitors is a Stored Procedure.