I'm rewriting the app which uses MS SQL database. There are few stored procedures which are being called. Let's say:
InsertFile #fileId, #fileName
DeleteFile #fileId
There is the case when you have to call these procedures. For example, request comes and to process this request you have to insert few files and delete few files:
InsertFile 100, file1
InsertFile 101, file2
DeleteFile 5
InsertFile 108, file8
Current app builds query as a string and then through SqlCommand executes it in one call.
In new app I'm using Entity Framework Core 6.0.5. I can call specific procedure in this way:
var fileIdParameter = new SqlParameter("#Id", fileId);
var fileNameParameter = new SqlParameter("#FileName", fileName);
await _db.Database.ExecuteSqlRawAsync("InsertFile #Id, #FileName", fileIdParameter, fileNameParameter);
But in case I described above there will be 5 separated calls to database. I want to do this in one database call.
How can I do this?
You can execute multiple statements in one SQL:
var sql = #"
EXEC InsertFile #id1, #FileName1;
EXEC InsertFile #id2, #FileName2;
EXEC DeleteFile #id3;
EXEC InsertFile #id4, #FileName4;
";
await _db.Database.ExecuteSqlRawAsync(sql,
new SqlParameter("#Id1", 100),
new SqlParameter("#FileName1", "file1"),
new SqlParameter("#Id2", 101),
new SqlParameter("#FileName2", "file2"),
new SqlParameter("#Id3", 5),
new SqlParameter("#Id4", 108),
new SqlParameter("#FileName4", "file8")
);
Related
Is it possible with a .NET client program, communicating with back-end SQL Server using the System.Data.SqlClient library, for the server to send an informational message to the client program in contexts that do not involve any error and for the client program to obtain the message that was sent?
create proc NewFoo
#value text,
#userid text
as
begin
insert foo
(name, createdby) values (#value, #userid);
declare #recordcount int
select #recordcount = count(*) from foo where createdby= #userid;
if #recordcount = 100
begin
-- let user know they've created their 100th record. Woo-hoo!!
end
end
Here is an additional pseudo-code example, closer to the actual use case; note the differing severity:
if #value > #maxallowed
begin
if #userlevel = 'manager'
raiserror('Exceeded max. Intentional?',10,1)
else
raiserror('Exceeds max. Your edit was not saved',11,1)
end
P.S. Without using an OUT parameter or RETURN value parameter in the client-side command object.
P.P.S. I am using the SqlDataAdapter.Update method and some of the code uses a DataReader on the SqlCommand object.
You can use PRINT in your stored procedure, and to capture it in the code by subscribing to the InfoMessage event in the SqlConnection object.
In your sql just put:
PRINT 'Exceeded max. Intentional'
and in your code grab it like this:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string msg = null;
connection.InfoMessage += (sender, e) => { msg = e.Message; };
// execute the procedure
// check msg
}
I am totally confused regarding how to use Stored Procedures using Entity Framework Core. If the stored procedure return an anonymous type, how do I retrieve the data? If the return type is not anonymous, what should I do? How do I add input/output parameters?
I am asking these questions because everywhere I look, I get a different answer. I guess EF Core is evolving rapidly and Microsoft is dabbling with a lot of ideas.
How do I add input/output parameters?
I'm going to answer this particular question of yours.
Below is a TSQL stored procedure with two input and two output parameters
CREATE PROCEDURE [dbo].[yourstoredprocedure]
-- Add the parameters for the stored procedure here
#varone bigint
,#vartwo Date
,#varthree double precision OUTPUT
,#varfour bigint OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- YOUR CODE HERE
SET #varthree = 10.02;
SET #varfour = #varone;
return;
END
Now To execute this stored procedure using Entity Framework Core
MyContext.Database
.ExecuteSqlCommand(#"EXECUTE [yourstoredprocedure] " +
" {0} " +
", {1} " +
",#varthree OUTPUT " +
", #varfour OUTPUT ", dataOne, dataTwo, outputVarOne, outputVarTwo);
var outputResultOne= outputVarOne.Value as double?;
var outputResultTwo= outputVarTwo.Value as long?;
You can pass your input simply using parameterized query as above. You can also create named parameters. such as for output parameters, I've created two named parameters as -
var outputVarOne = new SqlParameter
{
ParameterName = "#varthree ",
DbType = System.Data.DbType.Double,
Direction = System.Data.ParameterDirection.Output
};
var outputVarTwo = new SqlParameter
{
ParameterName = "#varfour ",
DbType = System.Data.DbType.Int64,
Direction = System.Data.ParameterDirection.Output
};
And This is how using EF Core you execute a stored procedure with input and output parameters. Hope this helps someone.
This solution provides methods that call a stored procedure and maps the returned value to a defined (non-model) entity. https://github.com/verdie-g/StoredProcedureDotNetCore
Microsoft address this issue:
"SQL queries can only be used to return entity types that are part of your model. There is an enhancement on our backlog to enable returning ad-hoc types from raw SQL queries." https://learn.microsoft.com/en-us/ef/core/querying/raw-sql
And here is the issue tracked in GitHub: https://github.com/aspnet/EntityFramework/issues/1862
you might use an extention like StoredProcedureEFCore
Then the usage is more intuitively.
List rows = null;
ctx.LoadStoredProc("dbo.ListAll")
.AddParam("limit", 300L)
.AddParam("limitOut", out IOutParam<long> limitOut)
.Exec(r => rows = r.ToList<Model>());
long limitOutValue = limitOut.Value;
ctx.LoadStoredProc("dbo.ReturnBoolean")
.AddParam("boolean_to_return", true)
.ReturnValue(out IOutParam<bool> retParam)
.ExecNonQuery();
bool b = retParam.Value;
ctx.LoadStoredProc("dbo.ListAll")
.AddParam("limit", 1L)
.ExecScalar(out long l);
I have a huge tornado app that was written in a blocking manner. I'm trying to convert my db calls to run async. I'm having lots of issues.
I keep the mongo calls in a top level folder called lib and in the app folder I keep all my views.
The error i'm getting
Traceback (most recent call last):
File "/Users/marcsantiago/staging_env/lib/python2.7/site-packages/tornado/web.py", line 1445, in _execute
result = yield result
File "/Users/marcsantiago/staging_env/lib/python2.7/site-packages/tornado/gen.py", line 1008, in run
value = future.result()
File "/Users/marcsantiago/staging_env/lib/python2.7/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "/Users/marcsantiago/staging_env/lib/python2.7/site-packages/tornado/gen.py", line 1017, in run
yielded = self.gen.send(value)
File "/Users/marcsantiago/pubgears/app/admin.py", line 179, in get
notes, start_date, stats, last_updated = self.db_data()
File "/Users/marcsantiago/pubgears/app/admin.py", line 76, in db_data
while (yield chain_slugs_updated.fetch_next):
AttributeError: 'NoneType' object has no attribute 'fetch_next'
So inside the lib folder I have this method.
def get_chains_updated(date):
slugs = []
# Chain class can't do aggregate could create a class instance if i want
cursor = db.chain.aggregate([
{'$match':{'last_update':{'$gt':date}}},
{'$group':{'_id':{'site':'$site'}, 'total':{'$sum':'$count'}}}
])
while (yield cursor.fetch_next):
res = yield cursor.next_object()
slugs.append(res['_id']['site'])
yield slugs
Later I call this method one of my views
chain_slugs_updated = yield chaindb.get_chains_updated(yesterday)
slugs = []
#for site in chain_slugs_updated:
while (yield chain_slugs_updated.fetch_next):
site = chain_slugs_updated.next_object()
slugs.append('%s' % (site, site))
notes.append('<strong>%s</strong> chains have been updated in the past 24 hours (%s).' % (chain_slugs_updated.count(), ', '.join(slugs)))
This is what it use to be when I was using pymongo
lib
def get_chains_updated(date):
slugs = []
# Chain class can't do aggregate could create a class instance if i want
results = db.chain.aggregate([
{'$match':{'last_update':{'$gt':date}}},
{'$group':{'_id':{'site':'$site'}, 'total':{'$sum':'$count'}}}
])
for res in results:
slugs.append(res['_id']['site'])
return slugs
view
chain_slugs_updated = chaindb.get_chains_updated(yesterday)
slugs = []
for site in chain_slugs_updated:
slugs.append('%s' % (site, site))
notes.append('<strong>%s</strong> chains have been updated in the past 24 hours (%s).' % (len(chain_slugs_updated), ', '.join(slugs)))
I have tons of code I have to translate to get this async working correctly, I would very much appreciate any help. Thanks.
To return a list of objects from get_chains_updated, you must either return slugs the list (Python 3) or raise gen.Return(slugs) (all Python versions). For more info, see Refactoring Tornado Coroutines.
I have a situation here where I have a deployed rdl file in the reporting server. The rdl file in question has a parameter.
I am using the rs.exec component to execute the report; whenever I remove the parameter from the rdl file, I successfully run the report from the stored procedure. When I add the parameter, all I keep getting is
\\server\R\subfolder\working\inputfile\example.batK00WE is not recognized as an internal or external operable program or batch file.
Here is what I did: I created a .rss file in VB (Please see code below)
Public Sub Main()
TRY
DIM historyID as string = Nothing
DIM deviceInfo as string = Nothing
DIM extension as string = Nothing
DIM encoding as string
DIM mimeType as string = "application/Excel"
DIM warnings() AS Warning = Nothing
DIM streamIDs() as string = Nothing
DIM results() as Byte
rs.Credentials = System.Net.CredentialCache.DefaultCredentials
rs.LoadReport(REPORTSERVER_FOLDER, historyID)
results = rs.Render(FORMAT, deviceInfo, extension, mimeType, encoding, warnings, streamIDs)
DIM stream As FileStream = File.OpenWrite(FILENAME)
stream.Write(results, 0, results.Length)
stream.Close()
Catch e As IOException
Console.WriteLine(e.Message)
End Try
End Sub
Afterwards... I wrote batch file that utilizes the rs.exec. Please see below:
"\\server\R\subfolder\working\app\rs.exe" -i \\server\R\subfolder\working\inputfile\coo.rss -s "http://server/ReportServer_MSSQLSERVER2" -v FILENAME="\\server\R\subfolder\working\inputfile\file.csv" -v REPORTSERVER_FOLDER="/FILE_REPORT/FILE" -t -v FORMAT="EXCEL" -e Exec2005
If you see the above script the rs.exec utilizes the path of the report server etc.
Finally I created a stored procedure that will run the report on the server and pass into the server the parameter value.
CREATE PROCEDURE [dbo].[test_sproc]
#ProcessID varchar(50)
AS
DECLARE #cmdsql varchar(1000)
Declare #id varchar(50)
Set #id=#SID
Set #cmdsql= '"\\server\R\subfolder\working\inputfile\example.bat"' + #id
exec master..xp_CMDShell #cmdsql
So here is my question is: How do I pass the parameter value from stored procedure to the report server? Where did I go wrong with my code?
I have a storedprocedure "SelectKP" (Datetime bt,Datetime et), please help to create a class and a repository for this procedure.
Look at this link:
How to execute a stored procedure within C# program
There is not a function generated manually, but it executes stored procedure
Here is simple example.
using (var db = new ModelEntities())
{
var query = db.ExecuteStoreQuery<ModelClass>("StoreProcedureName '" + UserId +"'");
}