How to call stored procedure in MVC controller ? [duplicate] - sql-server

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ASP.NET MVC: Best Way To Call Stored Procedure
I am developing MCV3 application.
I want to call store procedure in one of the controller of the applicaiton.
I have already saved store procedure in DB which I am using for applicaiton.
The Query is
Create Procedure ConvertLeadToCustomer1
#CompanyID int
as
begin
update Companies set __Disc__ = 'Customer' where CompanyID = #CompanyID
end
Now, I wan to call this procesure into controller...
namespace CRMWeb.Controllers
{
public class LeadController : Controller
{
private CRMWebContainer db = new CRMWebContainer();
//
// GET: /Lead/
public ViewResult Index()
{
//return View(db.Companies.ToList());
return View(db.Companies.OfType<Lead>().ToList());
}
public ActionResult Convert(int id)
{
// I want to write code here to call stored procedure...
}
}
}
How to call it ?

It's not different in mvc, if using ADO.net, below code call the stored procedure:
public ActionResult Convert(int id)
{
var connection = new SqlConnection("YOUR CONNECTION STRING");
var command = new SqlCommand("ConvertLeadToCustomer1",connection)
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#CompanyID", id);
connection.Open();
command.ExcuteNonQuery();
connection.Close();
}

Related

spring batch: efficient way to query results of a stored procedure within a tasklet

My goal is to query the results of a stored procedure (sql server) using a custom row mapper within a spring batch tasklet.
I am using Spring Boot (version 2.2.2.RELEASE) and Spring Batch (4.2.1.RELEASE).
Example:
Stored Procedure:
CREATE PROCEDURE storedProcName #numbers VARCHAR(max), #day DATE
AS
SET NOCOUNT ON;
SELECT something, something2, something3
FROM sometable
WHERE ids in (select value from string_split(#numbers,','))
AND day = #day
Custom Row Mapper:
public class CustomRowMapper implements RowMapper<CustomObject> {
private static final String SOMETHING = "something";
private static final String SOMETHING2 = "something2";
private static final String SOMETHING3 = "something3";
#Override
public CustomObject mapRow(ResultSet resultSet, int i) throws SQLException {
CustomObject customObject = new CustomObject();
customObject.setSomething(resultSet.getString(SOMETHING));
customObject.setSomething2(resultSet.getString(SOMETHING2));
customObject.setSomething3(resultSet.getInt(SOMETHING3));
return customObject;
}
}
Execute Stored Procedure and query the results:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("storedProcName")
.returningResultSet("test", new CustomRowMapper());
Map<String, Object> out = jdbcCall.execute(parameterSource);
List<CustomObject> customObjects = (List<CustomObject>) out.get("test");
It works fine, but there has to be a more efficient way to do this? But i didn't found anything useful.
there has to be a more efficient way to do this
Spring Batch provides the StoredProcedureItemReader which can call a given stored procedure and iterate over its results. It also allows you to use a custom mapper as the one you defined.
According to your code snippet, you call the procedure and get a List<CustomObject> over which you will need to iterate at some point. So I think a chunk oriented tasklet with a StoredProcedureItemReader<CustomObject> is a good choice for you:
#Bean
public StoredProcedureItemReader<CustomObject> itemReader() {
return new StoredProcedureItemReaderBuilder<CustomObject>()
.procedureName("storedProcName")
.rowMapper(new CustomRowMapper())
// set other properties
.build();
}
Edit: show how to return a synchronized reader
#Bean
#StepScope
public SynchronizedItemStreamReader<CustomObject> itemReader() {
StoredProcedureItemReader<CustomObject> reader = new StoredProcedureItemReaderBuilder<>()
.procedureName("storedProcName")
.rowMapper(new CustomRowMapper())
// set other properties
.build();
SynchronizedItemStreamReader<CustomObject> synchronizedReader = new SynchronizedItemStreamReader<>();
synchronizedReader.setDelegate(reader);
return synchronizedReader;
}

How to call stored procedure from EntityFramework 6 with 'hierarchyid' parameter

I am developing a service using WebApi2 and EntityFramework6.
I have a legacy SQLServer DB that my service must work with.
That DB is using heavily the 'hierarchyid' data type and this type is used internally in DB's stored procedures.
Seems like EF6 is not supporting 'hierarchyid' data type, so i used this fork that adds support for 'hierarchyid'.
While the retrieval from the DB is working great with the 'hierarchyid' type, my problem is with the Stored Procedures that need a 'hierarchyid' as a parameter.
The stored procedure looks like this:
CREATE PROCEDURE [dbo].[GetSomethingByNodeId]
(
#startingRoot HIERARCHYID
,#return HIERARCHYID OUTPUT
)
My client code for invoking this stored procedure looks like this:
var param1 = new SqlParameter("#startingRoot", new HierarchyId("/"));
var param2 = new SqlParameter{ ParameterName = "#return", Value = 0, Direction = ParameterDirection.Output };
var obj = context.Database.SqlQuery<HierarchyId>("GetSomethingByNodeId" #startingRoot, #return out", param1, param2).ToList();
But unfortunately calling this query throws an exception that says:
An unhandled exception of type 'System.ArgumentException' occurred in EntityFramework.SqlServer.dll
Additional information: No mapping exists from object type System.Data.Entity.Hierarchy.HierarchyId to a known managed provider native type.
Any ideas on how i can make this work?
Unfortunately, MetaType.GetMetaTypeFromValue does not allow to add types (all supported types are hardcoded).
I think you can accomplish your goal with nvarchar parameters and conversions.
In your C# code:
var param1 = new SqlParameter("#startingRoot", "/1/");
var param2 = new SqlParameter { ParameterName = "#return", Value = "", Size = 1000, Direction = ParameterDirection.Output };
var ids = context.Database.SqlQuery<HierarchyId>("GetSomethingByNodeId #startingRoot, #return out", param1, param2).ToList();
var returnedId = new HierarchyId(param2.Value.ToString());
In your procedure (I wrote some test code inside):
CREATE PROCEDURE [dbo].[GetSomethingByNodeId]
(
#startingRoot nvarchar(max), #return nvarchar(max) OUTPUT
)
as
declare #hid hierarchyid = hierarchyid::Parse('/1/')
select #return = #hid.ToString()
declare #root hierarchyid = hierarchyid::Parse(#startingRoot)
select #root as field
Also, you can try to use Microsoft.SqlServer.Types and SqlHierarchyId type like this:
var sqlHierarchyId = SqlHierarchyId.Parse("/");
var param1 = new SqlParameter("#startingRoot", sqlHierarchyId) { UdtTypeName = "HierarchyId" };
But, I think, this is wrong direction.
Oleg's answer is correct, hierarchyid is still not integrated to the EF very well, and you should operate with strings in .net. Here is one more approach which was used from the first days of HierarchyId datatype:
Stored Procedure:
CREATE PROCEDURE GetSomethingByNodeId
#startingRoot hierarchyid, -- you don't need to use nvarchar here. String which will come from the application will be converted to hierarchyId implicitly
#return nvarchar(500) OUTPUT
AS
BEGIN
SELECT #return = #startingRoot.GetAncestor(1).ToString();
END
In an application you are adding a partial class for your EF data context with the SP call using plain old ADO.NET. Probably you will write this other way or use Dapper instead, but the main idea here is passing parameter as string to SQL Server, and it will convert to the HierarchyId implicitly:
public partial class TestEntities
{
public string GetSomethingByNodeId(string startingRoot)
{
using (var connection = new SqlConnection(this.Database.Connection.ConnectionString))
{
var command = new SqlCommand("GetSomethingByNodeId", connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#startingRoot", startingRoot);
var outParameter = new SqlParameter("#return", SqlDbType.NVarChar, 500);
outParameter.Direction = ParameterDirection.Output;
command.Parameters.Add(outParameter);
connection.Open();
command.ExecuteNonQuery();
return outParameter.Value.ToString();
}
}
}
Then call this method as any other stored procedure using your EF context:
using (var context = new TestEntities())
{
var s = context.GetSomethingByNodeId("/1/1.3/");
}
UPD: here is how the extension method for legacy HierarchyId procedure call will look like with Dapper (as for me it looks much better than plain ADO.NET):
public string GetSomethingByNodeId(string startingRoot)
{
using (var connection = new SqlConnection(this.Database.Connection.ConnectionString))
{
var parameters = new DynamicParameters();
parameters.Add("startingRoot", startingRoot);
parameters.Add("return", null, DbType.String, ParameterDirection.Output, 500);
connection.Open();
connection.Execute("GetSomethingByNodeId", parameters, commandType: CommandType.StoredProcedure);
return parameters.Get<string>("return");
}
}

Dapper calls sp_executesql when I have parameters, is there a way around that?

When I call
connection.Execute(sql);
Dapper executes and everything is fine. When I call
connection.Execute(sql, new { UserId = _userId });
it executes with sp_executesql.
The issue is when it uses sp_executesql it's in its own scope. If it creates a temporary table, it's not accessible to subsequent queries that use the same connection. I could get around it by using global temporary tables, but I don't want to risk having two processes interfere with each other.
Does anybody know a way around that?
Update: I have the same problem when I use SqlCommand objects without Dapper. I wrote a unit test that illustrates the problem I'm having. WorksWithParameters fails with System.Data.SqlClient.SqlException : Invalid object name '#TEMP_OBJECTLIST'.
[TestFixture]
public class DapperTest
{
private const string TestObjectType = "S";
private const string ConnectionString = "XXXXXXXXX";
private static void CreateTempTableWithoutParameters(SqlConnection connection)
{
const string sql = "SELECT TOP 10 * INTO #TEMP_OBJECTLIST FROM sys.objects WHERE TYPE = 'S'";
connection.Execute(sql);
}
private static void UseTempTableWithoutParameters(SqlConnection connection)
{
const int expectedCount = 10;
const string sql = "SELECT COUNT(*) FROM #TEMP_OBJECTLIST WHERE TYPE = 'S'";
var count = connection.Query<int>(sql).First();
Assert.AreEqual(expectedCount, count);
}
private static void CreateTempTableWithParameters(SqlConnection connection)
{
const string sql = "SELECT TOP 10 * INTO #TEMP_OBJECTLIST FROM sys.objects WHERE TYPE = #OBJECT_TYPE";
connection.Execute(sql, new {OBJECT_TYPE = TestObjectType});
}
private static void UseTempTableWithParameters(SqlConnection connection)
{
const int expectedCount = 10;
const string sql = "SELECT COUNT(*) FROM #TEMP_OBJECTLIST WHERE TYPE = #OBJECT_TYPE";
var param = new {OBJECT_TYPE = TestObjectType};
var count = connection.Query<int>(sql, param).First();
Assert.AreEqual(expectedCount, count);
}
[Test]
public void WorksWithParameters()
{
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
CreateTempTableWithParameters(connection);
UseTempTableWithParameters(connection);
}
}
[Test]
public void WorksWithoutParameters()
{
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
CreateTempTableWithoutParameters(connection);
UseTempTableWithoutParameters(connection);
}
}
}
One way around the temp table scope problem is to create the temp table with one dummy column in the outer scope, then use alter table statements to add all the desired columns and use it.
Additionally, How to share data between procedures by Erland Sommarskog may be useful to you or another person looking for different options for sharing data.
I ran into the same problem with Dapper, but it's not Dapper's fault. sp_executesql is called by ADO.NET and this switches the "scope" so temp tables become invisible.
As a workaround:
//no parameters, so it runs without sp_executesql
conn.Execute("CREATE TABLE #temp BLAHBLAH");
//do your thing
conn.Execute("INSERT INTO #temp BLAHBLAH", parameters);
//cleanup (no parameters again)
conn.Execute("DROP TABLE #temp");

Dynamicly Change Database MVC3 and EntityFramework 4.1

I am working on an MVC3 application database first approach . I would like to use one connection string to connect to database, based on some string (company name). Example: I have in my MSSQL Express 2012 this db: my_database_microsoft, my_database_oracle and so on..(those databases have same structure). On login page I have 3 input fields: username,password,company. I know how to build connection string dynamic with SqlConnectionStringBuilder and then use it on EntityConnectionStringBuilder
string providerName = "System.Data.SqlClient";
string serverName = "MY-PC\\SQL2012";
string databaseName = "my_database_"+form[company].toString();
.....
.....
entityBuilder.Provider = providerName;
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata =#"res://*/Models.Model1.csdl|res://*/Models.Model1.ssdl|res://*/Models.Model1.msl";
using (EntityConnection conn =
new EntityConnection(entityBuilder.ToString()))
{
conn.Open();
// Console.WriteLine("Just testing the connection.");
conn.Close();
}
obracun_placEntities1.nameOrConnectionString = entityBuilder.ToString();
obracun_placEntities1 o = new obracun_placEntities1(entityBuilder.ToString());
I have made a partial class of my entety and give a constructor that take a nameOrConnectionString string as a parameter.
public partial class obracun_placEntities1
{
public string nameOrConnectionString { get; set; }
public obracun_placEntities1(string nameOrConnectionString)
: base(nameOrConnectionString ?? "obracun_placEntities1") { }
}
This works only in loginController but how can I use this in UsersController and all other controllers where I using obracun_placEntities1 db = new obracun_placEntities1(); > this take the default database from web.config. I would not like to save connection string to session or cookie and than pass it in every controler as a parameter.
private obracun_placEntities1 db = new obracun_placEntities1();
How can i achieve that i pass connection string in login controller and using this database in entire project.
One more problem occured when i want to use public static string nameOrConnectionString
and pass it to constructor. The problem is when I open application in Chrome and login as user1 I get all infromation from user1 database, but then I login in MS Explorere as user2 and get all data from user2 database. When i refresh chrome I get information from the user2 database not user1.
Model1.context.cs
public partial class obracun_placEntities1 : DbContext
{
public static string nameOrConnectionString { get; set; }
// public static string connection;
public obracun_placEntities1()
: base(nameOrConnectionString ?? "obracun_placEntities1")
{
}
Connecting to different Databases is best done using the DBconnection constructor on DBCOntext. If you look at the DBContext class you will see multiple constructor overloads. One allows the DBConnection to be supplied. So no entry in WEB.Config/App.Config is required.
See this post with sample code Same Context accessing different databases.
EDIT sample added:
public partial class obracun_placEntities1 : DbContext
{
// use THIS CONSTRUCTOR
protected obracun_placEntities1(DbConnection dbConnection, bool contextOwnsConnection)
: base(dbConnection, contextOwnsConnection)
{
}
}
}
// DONT USE THIS
// obracun_placEntities1.nameOrConnectionString = entityBuilder.ToString();
// obracun_placEntities1 o = new obracun_placEntities1(entityBuilder.ToString());`
// build the connection - note: it is NOT a connection string. it is a DBConnection!
conn = getDBConnection4SQLServer(DatabaseServer,Databasename)
obracun_placEntities1 o = new obracun_placEntities1(conn,true);
//====================================================================
public const string DefaultDataSource = "localhost";
public DbConnection getDBConnection4SQLServer(string dataSource, string dbName) {
var sqlConnStringBuilder = new SqlConnectionStringBuilder();
sqlConnStringBuilder.DataSource = String.IsNullOrEmpty(dataSource) ? DefaultDataSource : dataSource;
sqlConnStringBuilder.IntegratedSecurity = true;
sqlConnStringBuilder.MultipleActiveResultSets = true;
var sqlConnFact = new SqlConnectionFactory(sqlConnStringBuilder.ConnectionString);
var sqlConn = sqlConnFact.CreateConnection(dbName);
return sqlConn;
}
I finnaly getting it to work with this code.
My LoginController
[HttpPost]
public ActionResult Index(UPORABNIK model, FormCollection form)
{....}
public obracun_placEntities1(EntityConnection entityConnection)
: base(entityConnection, false)
{
}
I call EntityConnection conn = GetEntityConnDbName("ServerName", "FirmName").
_entities = new obracun_placEntities1(conn, false);
var uporabniki = from r in _entities.UPORABNIK.Where(r => r.ime == uporabnik && r.geslo == geslo && danes <= r.veljavnost).ToList()
select r;
I get the firmName from the post form so this work only in LoginController.But how can I use this constructor in all other Controllers? I get firm name only once in LoginControler, I tried to save it as cookie but then i can not read it in the constructor.
In my other Controller I use the default controller again. How can I tranfer conn to other Controllers?
private obracun_placEntities1 db = new obracun_placEntities1();
I would like to call like this
private obracun_placEntities1 db = new obracun_placEntities1(conn);
Or is there some better way?

WCF Service - return the output parameter from a stored procedure in Entity Model

I have a stored procedure for authentication, taking login and password and returning a string . I would like to call the stored procedure(sql server 2005) from my WCF data service ( using entity model and function imports ) and return the output parameter( string ) as the result .
I am using function import to map the stored procedure. How should I proceed ?
Finally, got the answer !! We have to use Output parameter , give it as an argument to the called stored procedure and finally just by a type cast we can use the value . ( My return format is JSON but it is equally valid for XML format)
Interface :
[OperationContract]
[WebInvoke(Method = "GET",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "authenticate/{login}/{pwd}")]
Implementation:
public string authenticate(string login, string pwd)
{
SteelcaseMigrationEntities entities = new SteelcaseMigrationEntities();
System.Data.Objects.ObjectParameter output =
new System.Data.Objects.ObjectParameter("out", typeof(string));
entities.authenticate_android(login, pwd, output);
//Console.Write(output.Value)
string result = (string)output.Value;
return result;
}

Resources