I wanna get the next automatically incrementing primary key, Here I understood to use T-SQL for doing that.
So I wrote the following method :
public int GetLastNewsID()
{
const string command = #"SELECT IDENT_CURRENT ('{0}') AS Current_Identity;";
int id = EntityModel.ExecuteStoreCommand(command, "News");
return id;
}
but it returns -1, whereas it must be 4 right now.
P.S:
When I execute below T-SQL in SQL Management Studio , it returns 4
USE T;
GO
SELECT IDENT_CURRENT ('NEWS') AS Current_Identity;
GO
You need to use ExecuteStoreQuery
public int GetLastNewsID()
{
const string command = #"SELECT IDENT_CURRENT ({0}) AS Current_Identity;";
var id = EntityModel.ExecuteStoreQuery<decimal>(command, "News").First();
return Convert.ToInt32(id);
}
The SQL query will return a decimal so you need to convert it to int.
Read part about return value:
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.executestorecommand.aspx
Related
I'm building a WinForms project in C# using a PostgreSQL database and the Npgsql framework.
For inserting a record, I need to return the ID of the new record. This SO question says to add SELECT SCOPE_IDENTITY() to the query string passed to cmd. So my query string looks like this:
string insertString = "INSERT INTO sometable (company_name, category, old_value, old_desc, new_value, new_desc, reference1, reference2) VALUES (#comp, #cat, #oldValue, #oldDesc, #newValue, #newDesc, #ref1, #ref2); SELECT SCOPE_IDENTITY();";
and then get the ID with something like
int modified = cmd.ExecuteNonQuery();
But that's likely SQL Server-specific. If I use that method, I get an exception at the above line saying, "fuction scope_identity() does not exist".
I wasn't able to find anything that seemed to address this on the Npgsql documentation.
Per the linked SO question and Denis' suggestions I've tried adding both
RETURNING id;
and
CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
to the query string, replacing SELECT SCOPE_IDENTITY(); with those statements in the code above. In both cases they work as intended in DBeaver on an insert, but in my C# code in my WinForm project, modified was set to "1".
NOTE: I re-titled the question and added more information about what I've done.
Add "returning idcolumn" to the end of the sql query, then run the command with the ExecuteScalar() method instead of ExecuteNonQuery(). It should return with an int.
string insert = "insert into table1 (col1) values (something) returning idcol";
int id = cmd.ExecuteScalar();
All the comments above were almost nearly spot on and got me to a solution but didn't exactly wrap it in a bow -- so I thought i'd post my implementation that works (with silly fake example tables of course).
private int? InsertNameIntoNamesTable(string name)
{
int? id = null;
using (var dbcon = new NpgsqlConnection(_connectionString))
{
dbcon.Open();
StringBuilder sb = new StringBuilder();
var sql = $#"
insert into names_table
(name)
values
({name})
returning id;
";
sb.Append(sql);
using (var cmd = new NpgsqlCommand(sql, dbcon))
{
id = (int)cmd.ExecuteScalar();
}
dbcon.Close();
}
return id;
}
I would like to execute a stored procedure with output parameter in Dot Net core 3.1.
I am using the ExecuteSqlInterpolated extension method of a DatabaseFacade class.
C# code to get employee's count.
string deptName="IT";
int? employeeCount = null;
Database.ExecuteSqlInterpolated($"exec dbo.usp_GetEmpCountByDept {deptName}, {employeeCount} out");
After execution employeeCount is null and -1 is the return value.
As some people requested stored proc code for reproducing the issue ,I have stored proc as below
CREATE PROCEDURE usp_GetEmpCountByDept
#Dept nvarchar(20),
#EmpCount int Output
AS
BEGIN
SELECT #EmpCount = COUNT(Id)
FROM [dbo].[Employees]
WHERE Department = #Dept
END
I have found other way which is working for me
Add Nuget package Microsoft.Data.SqlClient
Use ExecuteSqlRaw method instead
Below is the code
int? employeeCount = null;
string deptName="IT";
// Use Microsoft.Data.SqlClient namespace for SqlParameter.Visual studio will suggest "system.data.sqlclient" which does not work
var deptNameSQLParam = new Microsoft.Data.SqlClient.SqlParameter("#Dept", deptName);
var employeeCountSQLParam = new Microsoft.Data.SqlClient.SqlParameter("#EmpCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
Database.ExecuteSqlRaw("exec dbo.usp_GetEmpCountByDept #Dept={0}, #EmpCount={1} out", deptNameSQLParam, employeeCountSQLParam);
if (employeeCountSQLParam.Value != DBNull.Value)
{
employeeCount = (int)employeeCountSQLParam.Value;
}
change this
string deptName="IT";
int? employeeCount = null;
Database.ExecuteSqlInterpolated($"exec dbo.usp_GetEmpCountByDept {deptName}, {employeeCount} out");
to this (worked for me with .NET 5, don't know about earlier)
string deptName="IT";
// make explicit SQL Parameter
var output = new SqlParameter("#EmployeeCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
// use output parameter instead
Database.ExecuteSqlInterpolated($"exec dbo.usp_GetEmpCountByDept {deptName}, {output} out");
// assign output
int? employeeCount = output.Value;
ExecuteSqlInterpolated Executes the given SQL against the database and returns the number of rows affected.
You cannot expect data to be returned with this method.
Refer this MSDN documentation.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.executesqlinterpolated?view=efcore-3.1
I am using asp net core in my project with dapper ORM and Postgresql as database. I want to check if a user with a particular UUID (GUID in C#) value exists in the database or not. Following is my code to check the same:
public async Task<bool> DoesRecordExistAsync(Guid columnValue)
{
bool doesRecordExist;
doesRecordExist = await _connection.ExecuteScalarAsync<bool>("SELECT * FROM employee_master WHERE employee_id = #columnValue;", new { columnValue });
return doesRecordExist;
}
Note: _connection is IDbconnection instance.
When executing the statement, I am getting the below error:
Object must implement IConvertible.
Is there anything wrong with the above code.
Your query "SELECT * FROM ...." is returning matching rows from database; NOT the bool that you are expecting. The ExecuteScalar will return first column of first row. One cannot guarantee that that column is always a Boolean; and basically, you are not expecting value of that column either.
The query should be something like select exists(select 1 from... as explained here.
It will return true/false which you can then handle with await _connection.ExecuteScalarAsync<bool>(.......
So, the new code becomes:
bool exists = await _connection.ExecuteScalarAsync<bool>("select exists(select 1 from....);
return exists;
Alternatively (slow; not recommended), you should change your query to something SELECT COUNT(*) FROM ... to get the count of records. Read more about optimization here.
Then you can cast it to bool something like below:
int count = await _connection.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM..........);
bool exists = count == 0 ? false : true;
return exists;
Try to use the next query :
SELECT EXISTS(SELECT 1 FROM employee_master WHERE employee_id = #columnValue)
Faced with strange problem:
string sql = $#"SELECT * FROM SearchLogs
WHERE CHECKSUM(#query) = cs_SearchTerm
AND Website = #website";
return await Connection.QueryFirstOrDefaultAsync<SearchLog>(sql,
param: new { query, website });
Record 100% exists in the database, but I got null. cs_SearchTerm is computed, int, nullable field. Then I tried:
DECLARE #term nvarchar(500)
SET #term = #query
SELECT * FROM SearchLogs
WHERE CHECKSUM(#term) = cs_SearchTerm AND Website = #website
But got same result. Then I tried split into two operations:
private async Task<int> SqlCheckSumAsync(string query)
{
string sql = #"SELECT CHECKSUM(#query)";
return await Connection.ExecuteScalarAsync<int>(sql, param: new { query }, transaction: Transaction);
}
string sql = $#"SELECT * FROM Search_Master
WHERE cs_SearchTerm = #checksum
AND Website = #website";
int checksum = await SqlCheckSumAsync(query);
return (await Connection.QueryFirstOrDefaultAsync<Search_Master>(sql,
param: new { checksum, website },
transaction: Transaction));
But still got not positive result. I wonder what I doing wrong? Why I can't pass param into SQL scalar?
From the comments, this works:
SELECT * FROM SearchLogs WHERE cs_SearchTerm = CHECKSUM('abc') AND Website = 'ICF'
So: that tells me that you computed your checksums using varchar inputs. This is very important, because CHECKSUM('abc') gives a completely different answer than CHECKSUM(N'abc'):
select checksum('abc') as [Ansi], checksum(N'abc') as [Unicode]
gives:
Ansi Unicode
----------- -----------
34400 1132495864
By default, dapper uses nvarchar (because .NET strings are utf-16). So we need to tell dapper to pass that as an ANSI string; fortunately this is simple:
return await Connection.ExecuteScalarAsync<int>(sql,
new { query = new DbString { Value = query, IsAnsi = true} },
transaction: Transaction);
Dapper's DbString type allows fine-grained control over how strings are sent, including both whether they are unicode or ANSI, and whether they are fixed width (and if so: what) or variable width.
I made a stored procedure in sql server 2008 which gives me the changes made to a table. I am using Linq to SQL to use this table in C#.
my stored procedure is
CREATE PROCEDURE dbo.getlog
-- Add the parameters for the stored procedure here
#p1 int = 0,
#p2 int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #from_lsn binary(10), #to_lsn binary(10)
SET #from_lsn =
sys.fn_cdc_get_min_lsn('dbo_User_Info')
SET #to_lsn = sys.fn_cdc_get_max_lsn()
SELECT ID_number, Name, Age FROM cdc.fn_cdc_get_all_changes_dbo_User_Info
(#from_lsn, #to_lsn, N'all');
END
GO
The above procedure runs fine in sql server. However when i run this statement using linq in C#
mytestDataContext obj = new mytestDataContext();
var test=obj.ExecuteCommand("dbo.getlog");
foreach( var abc in test)
{}
I get this error
Error 1 foreach statement cannot operate on variables of type 'int'
because 'int' does not contain a public definition for
'GetEnumerator'
ExecuteCommand returns an int.. not your results.
See MSDN here: http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executecommand.aspx
public int ExecuteCommand(
string command,
params Object[] parameters
)
I think you're after ExecuteQuery.
ExecuteCommand method returns Int32 and you can't use magical foreach loop using a simple integer.
Return Value
Type: System.Int32
The number of rows modified by the executed command.
I'm not too much familiar with DataContext class but you can use DataContext.ExecuteQuery which returns IEnumerable<TResult> and you can use foreach loop with it.
Return Value
Type: System.Collections.Generic.IEnumerable<TResult>
A collection of objects returned by the query.
I don't know why do you use foreach-statement, but method 'ExecuteCommand' returns int value, and foreach cycle need object that implements IEnumerable
I may be assuming too much, but if you are doing the C# with a recent version of Visual Studio, rather directly specifying the T-SQL call to run the stored procedure as string of literal text, you can drag and drop the stored procedure onto the LINQ to SQL modelling window. This will add it to the LINQ to SQL data context.
int param1 = 1;
int param2 = 2;
mytestDataContext obj = new mytestDataContext();
var test=obj.getlog(param1, param2);
foreach( var abc in test)
{
/* code inside loop */
}
The same technique can be used for calling user-defined functions.
Doing this will reduce typing and provide intellisense to help with calling the stored procedures and SQL functions.
You can use this library:
https://github.com/mrmmins/C-StoreProcedureModelBinding
Returns the values as a List, you only need create a simple class with the names and values types, like:
var productos = DataReaderT.ReadStoredProceadures<MyCustomModel>(myDbEntityInstance, "dbo.MySPName", _generic);
and MyCumtomModel class is something like:
public int id {get; set;}
public int salary {get; set;}
public string name {get; set;}
public string school {get; set;}
and generic like:
List<Generic> _generic = = new List<Generic>
{
new Generic
{
Key = "#phase", Type = SqlDbType.Int, Value = "207"
}
}
};
And now, your products has the options like: products.First(), products.Count(), foreach etc.