Using last identity inserted value to insert in another table - asp.net

I am facing this weird problem and spent several hours. Little help would be greatly appreciated.
This is an ASP.NET MVC app. For simplicity, I have two SQL tables, Employee (ID, Name, JoiningDate) and Benefits (ID, EmployeeID). Both IDs are identity colums. When a new employee joins the company, an entry is created in the Employee table as well as Benefits table.
The stored procedure looks like this
alter procedure usp_CreateEmployee
#Name nvarchar(100),
#JoiningDate datetime
as
declare #lastIdentity int
insert into Employee(Name, JoiningDate) values(#Name, #JoiningDate)
select #lastIdentity = ident_current('Employee')
insert into Benefits(EmployeeID) values(#lastIdentity)
C# side I am using Dapper
var parameters = new DynamicParameters();
parameters.Add("#Name", name);
parameters.Add("#JoiningDate", joiningDate);
affectedRows = connection.Execute("usp_CreateEmployee", parameters, null, commandType: CommandType.StoredProcedure);
When I execute the stored procedure in SSMS, everything works perfect. (ident_current returns the last inserted id). However, when the user interface creates employee (through razor page), a NULL gets inserted as EmployeeID in Benefits table. Employee table shows correct Employee ID.
Doesn't look like a SQL problem. Is there anything wrong with my code? Could this be Dapper related (though I dont think so)?

I think the problem was on the "ident_current". Please refer here: https://sqlperformance.com/2014/01/t-sql-queries/ident-current
alternatively, you may try below sql script.
alter procedure usp_CreateEmployee
#Name nvarchar(100),
#JoiningDate datetime
as
declare #lastIdentity int
insert into Employee(Name, JoiningDate) values(#Name, #JoiningDate)
select top(1) #lastIdentity=ID from Employee where Name=#Name
order by ID desc
insert into Benefits(EmployeeID) values(#lastIdentity)

Related

Entity Framework Inserting Multiple Copies of Same Data

I am having a strange problem with Entity Framework and SQL Server that I cannot figure out.
I am building an online store in ASP.NET MVC 5.
I am inserting statistics about a search into a table called SearchResults - it has this structure:
[SearchID] [int] IDENTITY(1,1) NOT NULL,
[SearchTerm] [varchar](5000) NULL,
[SearchDate] [datetime] NULL,
[Results] [int] NULL
I am just doing a simple EF insert with this C# code in the Search action of a controller, which gets posted to with a search term:
var s = new SearchResult() { SearchTerm = search, SearchDate = DateTime.Now, Results = results };
db.SearchResults.Add(s);
db.SaveChanges();
Results is an int with the count of the products found by the search.
Whenever I do a search, the same search gets inserted exactly 3 times, with slightly different times for each insert. The weird part is that occasionally, it will only insert one entry (as expected). I can't figure out why it is doing this.
I've run an SQL trace, and when it inserts 3, there is only one call to the DB. This is what is in the trace:
exec sp_executesql N'INSERT [dbo].[SearchResults]([SearchTerm],[SearchDate], [Results])
VALUES (#0, #1, #2)
SELECT [SearchID]
FROM [dbo].[SearchResults]
WHERE ##ROWCOUNT > 0 AND [SearchID] = scope_identity()',N'#0 varchar(5000),#1 datetime2(7),#2 int',#0='dew',#1='2015-02-16 16:32:53.4649185',#2=2
The weird part is the datetime shown in the insert is the value for the third insert.
I am at a complete loss for why this is happening. I've tried everything I can think of, but I am still getting repeats on insert.
Maybe you're looking at the wrong piece of code.
Had you logged or debugged the calls to the controller, how many times the first snippet of code you posted get executed?

Denormalization - maintaining redundancy by using triggers in Oracle

I have a task for my university project where I have denormalized a table and now I have to implement a trigger in order to maintain redundancy. After following the book accordingly I'm stuck on the last step and I can't get my mind around it.
I have two tables - Supplier and Catalogue
**Supplier**
SupplierCode (primary key)
SupplierName
**Catalogue**
SupplierCode (foreign key)
CatalogueCode (both SupplierCode and CatalogueCode form a primary key)
CatalogueName
DateCreated
SupplierName (redundant column for denorm purposes)
When a new row is inserted in the Catalogue table I want that SupplierName field in the same table gets populated based on the SupplierCode which was inserted.
What I have so far:
We need to create a package as well
create or replace PACKAGE "PACK" AS
SUPPCODE NUMBER:=0;
SUPPNAME VARCHAR2(50);
END;
I have created a BEFORE INSERT TRIGGER on Catalogue table
create or replace TRIGGER CatalogueBefore
BEFORE INSERT OR UPDATE ON CATALOGUE
FOR EACH ROW
BEGIN
PACK.SUPPCODE:=:NEW.SUPPLIERCODE;
END;
I've then added an AFTER INSERT TRIGGER on Catalogue table
create or replace TRIGGER "AddNewCatalogue"
AFTER INSERT ON CATALOGUE
FOR EACH ROW
DECLARE
V_SUPPNAME SUPPLIER.SUPPLIERNAME%TYPE;
BEGIN
SELECT SUPPLIERNAME INTO V_SUPPNAME
FROM SUPPLIER
WHERE SUPPLIERCODE= PACK.SUPPCODE;
PACK.SUPPNAME:=V_SUPPNAME;
END;
After all of this I have the Supplier name held in my package in PACK.SUPPNAME but my question is how do I now use this variable when inserting a new row in the Catalogue table? I'm stuck at this for some time now and I'm probably missing something obvious. :) Any help is appreciated. Thanks.
P.S Please ignore uppercase vs. lowercase letters - I've just translated the names from my native language. :)
Thanks
I have denormalized a table and now I have to implement a trigger in order to maintain redundancy
[...]
When a new row is inserted in the Catalogue table I want that SupplierName field in the same table gets populated based on the SupplierCode which was inserted.
If those are your only requirements, all you need is one trigger to alter the record with the data from the second table. Something like that, maybe:
CREATE OR REPLACE TRIGGER CatalogueBefore
BEFORE INSERT OR UPDATE ON Catalogue
FOR EACH ROW
BEGIN
SELECT S.SupplierCode INTO :NEW.SupplierCode
FROM Supplier S
WHERE S.SupplierName = :NEW.SupplierName;
END;
That way, on update or insert into the table Catalogue, the SupplierCode will be overwritten by the code coming from Supplier table. See http://sqlfiddle.com/#!4/d79e0/1 for a live example.
Some notes though:
For this to work, Supplier.SupplierName should be an unique index too. In fact, the all thing is based on the assumption this is a natural key.
You very probably need an other trigger to deal with updates in the Supplier table. Say, when one supplier's name will change, the new name will have to be propagated to Catalogue.

Entity framework - history table

I have the following 2 tables defined in a SQL Server database:
create table Project
(
Id int,
Name varchar(100)
IdIdentity int
)
create table ProjectHistory
(
Id int,
Name varchar(100)
IdIdentity int,
DtChange datetime
)
When I updated "Project" I would like to insert new record to table ProjectHistory with setting DtChange to actual date.
I use Entity Framework 4.1. Can you suggest me some way how this can be accomplished?
There is possibility change sql that generate Entity Framework when saving changes.
The conventional way would be to use an update trigger on the project table.
CREATE TRIGGER trgProjectUpdate
ON Project
AFTER UPDATE
AS
BEGIN
insert ProjectHistory (id, name, dtchange)
select id, name, GETDATE() from deleted
END

Concurrency logic in asp.net

I have a web form that inserts a row into an SQL Server 2008 table. Table has a field which must be unique, but for some reasons we are not allowed to use autoincrement utility. Instead of this I select the maximum of this field and increment it from code and insert the row with that id.
The problem is, if more than one person uses this form simultaneously, there occurs a concurrency problem and same id will be inserted to the table. I do not know how to solve this issue in a asp.net web project because all users have their own session and threads.
Any idea on how to manage concurrency problems in asp.net project will be very useful.
Thanks.
If you perform the insert into a stored procedure, or in a transaction in the code, I don't see a reason to have problems with concurrency. Something like:
BEGIN TRAN
DECLARE #MAX int
SELECT #MAX = MAX(ID) FROM MyTable
INSERT INTO MyTable (#MAX, VAlue, Value2..)
COMMIT TRAN
or
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction;
transaction = connection.BeginTransaction("InsertRow");
... perform insert using command object ...
transaction.Commit();

How can I create a LINQ statement where the table name (FROM) and column name (SELECT) is variable?

In my programming task I've gone down a dark alley and wished I hadn't, but there is no turning back now.
I'm building up a SQL statement where the table name, column name and id value are retrieved from query string parameters i.e. ("SELECT [{0}] FROM [{1}] WHERE [Id] = {2};", c, t, id)
But it isn't as bad as it looks, I'm protected:
Only authenticated users (i.e. signed in users) can execute the Page_Load
I'm checking that both the table and the column exists beforehand
(using GetSchema etc.)
I'm checking that the Id is an integer beforehand
All my tables have Id columns
The database connection is reasonably secure
The field value is expected to be of type NVARCHAR(4000) or NVARCHAR(MAX) so I'm avoiding ExecuteScalar and I'm trying out LINQ ExecuteQuery because I like LINQ. But I'm a bit out of my depth again.
I've got this far:
Dim db As New MyDataContext
Dim result = db.ExecuteQuery(Of ITable)("SELECT [{0}] FROM [{1}] WHERE [Id] = {2};", c, t, id)
Is this the right way to go?
How do I get first row and first column value?
Is there a better alternative?
P.S. It's a SQL Server 2005 database
Any help appreciated.
Thanks.
SQL Server requires the tables ans columns to be statically known. You can't provide them using command parameters. You can't say
select * from #tableName
because the table name can't be a variable.
You need to build the SQL string with C# ensuring proper escaping of identifiers. Escaping works like this:
var escaped = "[" + rawUntrustedUserInput.Replace("]", "]]") + "]";
This is safe.

Resources