I am new in Dapper.
I want to select dynamic items from conn.Query<>() that it execute a stored procedure in SQL.
My code is similar to :
connection.Open();
var parameters = new DynamicParameters();
parameters.Add("#name", x.GetValue<string>("Name"));
parameters.Add("#lastname", x.GetValue<string>("LastName"));
var query = connection.Query<Costumer>(sql: "GetCostumer", param: parameters, commandType: CommandType.StoredProcedure);
and my stored procedure is similar to:
select * from Costumer where name = #Name and lastname = #lastname
In entity framework i can resolve my problem by this code:
IQueryable<User> query = container.GetQuery<Users>();
var finalResult = (from item in query
select new {
item.name,
item.lastname
}).ToList();
If the question is about selecting just the two columns, then at the simplest level, you can just do exactly what you are already doing, i.e.
var finalResult = (from item in query
select new {
item.Name,
item.LastName
}).ToList();
Note that this does fetch all the columns back to the client, and then re-maps there. If you want to do the column filtering at the server, then you'll need to use something like INSERT EXEC with a subsequent SELECT, i.e.
DECLARE #table_var TABLE (cols)
INSERT INTO #table_var (cols) EXEC proc {args}
SELECT foo, bar FROM #table_var
Although a cleaner implementation might be to expose a UDF - then you can just use:
SELECT foo, bar FROM dbo.SomeFunction(args);
As an aside, if the <Costumer> type doesn't already exist, there is a way to avoid the intermediate type via dynamic and the non-generic Query method, but it is actually slightly less efficient than the above, so should be used sparingly.
Note also that you don't need DynamicParameters in this case; you could just use:
var parameters = new {
name = x.GetValue<string>("Name"),
lastname = x.GetValue<string>("LastName")
};
var query = connection.Query<Costumer>("GetCostumer", parameters,
commandType: CommandType.StoredProcedure);
Related
The following SQL code works perfectly in [QTY] database. it deletes all the rows in [Table1], then runs the stored procedure [test] and inserts the result into [Table1].
I want to be able to run this code from an MVC controller. How can I achieve this? Thank you.
USE [QTY]
GO
DECLARE #return_value int
Delete from Table1
INSERT INTO Table1
EXEC #return_value = [dbo].[test]
#Month = N'M4',
#Forecast = '2019-04-30'
SELECT 'Return Value' = #return_value
GO
Try to use FromSql method which enables you to pass in a SQL command to be executed against the database to return instances of the type represented by the DbSet .
// Format string
var author = db.Authors.FromSql("SELECT * From Authors Where AuthorId = {0}", id).FirstOrDefault();
// String interpolation
var author = db.Authors.FromSql($"SELECT * From Authors Where AuthorId = {id}").FirstOrDefault();
The DbContext exposes a Database property which includes a method called ExecuteSqlCommand. This method returns an integer specifying the number of rows affected by the SQL statement passed to it.
using(var context = new SampleContext())
{
var commandText = "INSERT Categories (CategoryName) VALUES (#CategoryName)";
var name = new SqlParameter("#CategoryName", "Test");
context.Database.ExecuteSqlCommand(commandText, name);
}
You could take some time on the tutorial Executing Raw SQL Queries
I am not sure if this is possible but I have not been able to come across clear documentation for this use case. I am using F# 4 and the FSharp.Data.SqlClient library to connect to SQL Server 2016. I am wanting to call a stored procedure that returns multiple tables and turn those tables into the corresponding records. In this case the first table is made up of items and the second table is made up of customers.
My instinct is that it should look something like this:
let items, customers = cmd.Execute()
My gut is that items would be an IEnumerable<item> and customers would be an IEnumerable<customer> where item and customer are both Record types. What it appears is happening though is that FSharp.Data.SqlClient is only seeing the first returned table from the stored procedure. I am working on a SQL Server 2016 Developer instance. Here is the T-SQL to setup the example:
create table Item (
ItemID int identity(1, 1) primary key,
ItemName nvarchar(50)
)
go
create table Customer (
CustomerID int identity(1, 1) primary key,
CustomerName nvarchar(50)
)
go
insert into Item (ItemName) values ('A');
insert into Item (ItemName) values ('B');
insert into Item (ItemName) values ('C');
insert into Customer (CustomerName) values ('Gary');
insert into Customer (CustomerName) values ('Sergei');
insert into Customer (CustomerName) values ('Elise');
go
create procedure dbo.ExampleProcedure
as
begin
set nocount on;
select
ItemID,
ItemName
from Item
select
CustomerID,
CustomerName
from Customer
end;
And here is the F# script that I am testing with. It shows what I would like to be able to do but I get a compile error on the last line:
#r "../packages/FSharp.Data.SqlClient.1.8.2/lib/net40/FSharp.Data.SqlClient.dll"
#r "../packages/FSharp.Data.2.3.2/lib/net40/FSharp.Data.dll"
#r "System.Xml.Linq.dll"
open FSharp.Data
[<Literal>]
let connStr =
"Data Source=**connection string**;"
type queryExample = SqlProgrammabilityProvider<connStr>
do
use cmd = new queryExample.dbo.ExampleProcedure(connStr)
let items, customers = cmd.Execute()
I am wanting items to correspond to the first returned table and customers to correspond to the second returned table. The intellisense suggests that FSharp.Data.SqlClient is only seeing the first table. When I hover over cmd.Execute() the popup says "This expression was expected to have type 'a*'b but here has type System.Collections.Generic.IEnumerable<SqlProgrammabilityProvider<...>.dbo.ExampleProcedure.Record>". If I do the following I get access to the Items query in the stored procedure:
// Learn more about F# at http://fsharp.org. See the 'F# Tutorial' project
// for more guidance on F# programming.
#r "../packages/FSharp.Data.SqlClient.1.8.2/lib/net40/FSharp.Data.SqlClient.dll"
#r "../packages/FSharp.Data.2.3.2/lib/net40/FSharp.Data.dll"
#r "System.Xml.Linq.dll"
open FSharp.Data
[<Literal>]
let connStr =
"Data Source=**connection string**;"
type queryExample = SqlProgrammabilityProvider<connStr>
do
use cmd = new queryExample.dbo.ExampleProcedure(connStr)
for item in cmd.Execute() do
printfn "%A" item.ItemID
Is this even possible? Is my approach wrong? I could not find clear documentation on this use case but I thought it would be common enough it would be covered.
Update
Just to clarify what I am trying to achieve I am showing how I solve this in C#. In C# I create a DataSet object and populate it with the results of the Stored Procedure. From there I pick out the individual tables to work with. After extracting the tables I then use LINQ to transform the rows into the corresponding objects. It often looks something like the following:
using System.Data;
using System.Data.SqlClient;
var connStr = "**connection string**"
var sqlConnection = new SqlConnection(connStr );
var sqlCommand = new SqlCommand("ExampleProcedure", sqlConnection);
sqlCommand.CommandType = CommandType.StoredProcedure;
var dataSet = new DataSet();
var adapter = new SqlDataAdapter(sqlCommand);
adapter.Fill(dataSet);
var itemsTable = dataSet.Tables[0];
// Turn the itemsTable into a List<Item> using LINQ here
var customersTable = dataSet.Tables[1];
// Turn the customersTable into List<Customer> using LINQ here
I find this to be overly verbose for such a simple thing as extracting the individual tables but perhaps I am too sensitive to code clutter. I know that F# must have a more elegant and terse way to express this.
I don't know F#, however this is a data access problem.
When a stored procedure returns multiple resultsets, you need to access they in sequence, one by one.
cmd.ExecuteReader() returns an instance of a datareader pointing to the first resultset. You need to process this resultset, may be filling a list with instances of a custom class, than you call the method "NextResult" and you will have access to the next resultset and so on.
A reference for the method "NextResult": https://msdn.microsoft.com/pt-br/library/system.data.sqlclient.sqldatareader.nextresult(v=vs.110).aspx
It is my understanding that there is no elegant way to call a stored procedure in EF6 using table-valued parameters. Can someone verify or discredit my suspicion?
Table-valued type:
CREATE TYPE MyTestType AS TABLE
(
ID INT,
String VARCHAR(30)
);
Stored procedure:
CREATE PROCEDURE MyTestProc
#TestVar MyTestType READONLY
AS
BEGIN
SELECT * FROM #TestVar
END
C# EF call:
var testTable = new DataTable();
testTable.Columns.Add("ID", typeof (int));
testTable.Columns.Add("string", typeof (string));
testTable.Rows.Add(1, "Row1");
var parameter = new SqlParameter("#TestVar", SqlDbType.Structured);
parameter.Value = testTable;
parameter.TypeName = "dbo.MyTestType";
var results = dbContext.Database.SqlQuery<TestObject>("exec dbo.MyTestProc #TestVar", parameter).ToList();
The code above is working, I would just like to see if there is a more elegant way of executing it. I feel like I should be allowed to do something like
var results = dbContext.MyTestProc(testTable).ToList();
Edit
I came across this article which has a slightly cleaner way of handling the data table, but still hoping for a cleaner way to call my stored procedure with the table-valued parameter.
Code.msdn - Stored Procedure with Table-Valued Parameter in EF and ASP.NET MVC
When I execute Stored Procedure
SELECT * FROM Users
INNER JOIN BloodBankUser ON Users.UserID = BloodBankUser.UserID
It gives me result fine.
but now on .net side
dt.Rows[0]["Address"].ToString();
this gives me Address of table BloodBankUser
dt.Rows[0]["Users.Address"].ToString();
when I debug this statement it execute error
Column 'Users.Address' does not
belong to table.
How can I get Value of Users.Address
While the first answer would be to change your SQL Query to specify a distinct name for each of your field, it is still possible to retrieve the table name associated with your field.
In this example, I am not filling a DataTable using a DataAdapter, but rather I am using the SqlDataReader.
Be aware that this may fail if you are unable to retrieve the database schema for any reason
When calling ExecuteReader on a SqlCommand, there is an overload that allows you to specify a CommandBehavior. In our case, the behavior that we want is CommandBehavior.KeyInfo.
var reader = command.ExecuteReader(CommandBehavior.KeyInfo);
Now, on the reader, you can invoke the GetSchemaTable method. It returns a DataTable that contains the structure of your query.
var schema = reader.GetSchemaTable();
You can read about that table on MSDN.
Our goal now is to match the field and table against its ordinal position in the column list. Three fields from the schema table are relevant to your interest:
ColumnName
BaseTableName
ColumnOrdinal
You can then create an extension method to do that reading:
public static T Field<T>(this SqlDataReader reader, DataTable schema, string table, string field)
{
// Search for the ordinal that match the table and field name
var row = schema.AsEnumerable().FirstOrDefault(r => r.Field<string>("BaseTableName") == table && r.Field<string>("ColumnName") == field);
var ordinal = row.Field<int>("ColumnOrdinal");
return (T)reader.GetValue(ordinal);
}
You can then call that extension method
using (SqlConnection connection = new SqlConnection("your connection string"))
{
connection.Open();
using (SqlCommand command = new SqlCommand("SELECT * FROM Users INNER JOIN BloodBankUser ON Users.UserID = BloodBankUser.UserID;", connection))
using (var reader = command.ExecuteReader(CommandBehavior.KeyInfo))
{
var schema = reader.GetSchemaTable();
while (reader.Read())
{
Console.WriteLine(reader.Field<string>(schema, "Users", "Address"));
}
}
}
Rename the FIELD in the output (Select FIELDNAME as NEWNAME)
You specify the column names rather than use SELECT * FROM You will then be able to do the following
Select User.Username,
User.Address as 'UserAddress',
BloodBankUser.Address as 'BloodbankAddress'
FROM Users
INNER JOIN BloodBankUser ON Users.UserID = BloodBankUser.UserID
Avoid the use of * in SELECT queries. Select only the columns you need and name them explicitly to avoid ambiguity,
Insead of SELECT *... specify the columns you want explicitly, and alias those that may duplicate
SELECT Users.Address as UsersAddress
This question already has answers here:
T-SQL stored procedure that accepts multiple Id values
(5 answers)
Closed 7 years ago.
How can I construct a stored procedure that will allow me to pass (for example) an #IDList so that I can write:
Select * from Foo Where ID in #IDList
Is this doable?
With SQL2005 and above you can send an array from code directly.
First create a custom type
CREATE TYPE Array AS table (Item varchar(MAX))
Than the stored procedure.
CREATE PROCEDURE sp_TakeArray
#array AS Array READONLY
AS BEGIN
Select * from Foo Where ID in (SELECT Item FROM #array)
END
Then call from code passing in a DataTable as the array
DataTable items = new DataTable();
items.Columns.Add( "Item", typeof( string ) );
DataRow row = items.NewRow();
row.SetField<string>( "Item", <item to add> );
items.Rows.Add( row );
SqlCommand command = new SqlCommand( "sp_TakeArray", connection );
command.CommandType = CommandType.StoredProcedure;
SqlParameter param = command.Parameters.Add( "#Array", SqlDbType.Structured );
param.Value = items;
param.TypeName = "dbo.Array";
SqlDataReader reader = command.ExecuteReader();
see this answer...
T-SQL stored procedure that accepts multiple Id values
Write the individual IDs to table B, all with the same "key" (a GUID perhaps).
Then, your query against table A would include
where ID in (select ID from B where key = #TempKey)
(You might then delete the keys if you are finished with them. Or, timestamp them and have a sql job do it later.)
Pros:
You don't send a string, which could expose you to sql injection in some circumstances.
Depending on your other app logic, you don't have to track or write the possibilities all at once.
Cons:
It could be extremely inefficient, especially under heavy loads.