Insert using DataContext and getting ID CSLA C# - c#

How can I retrieve the ID during an insert in C# using CSLA? The stored procedure does an insert into the database table and then has a SELECT #Id (which is set with the SCOPE_IDENTITY()) after the insert.
This is the code to insert in C#:
using (var mgr = ContextManager<PersonDataContext>.GetManager("TestDB"))
{
var results = mgr.DataContext.up_StoredProcToInsert(
"David",
30,
);
}

try below example based on CRUD Operations using Stored Procedure in Entity Framework:
SP:
CREATE PROCEDURE SP_Ins_Test
#name nchar(10)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.test(name)
SELECT #name
SELECT SCOPE_IDENTITY() AS ResultId
END
GO
C# code:
DemoDB_JRDevEntities db = new DemoDB_JRDevEntities();
SP_Ins_Test_Result r=db.SP_Ins_Test("Raj").First();
string id= r.ResultId.ToString();

Although the sample doesn't use a stored procedure, relying on EF to do the work itself, you can look at the EF data access layer in the ProjectTracker sample to see how an insert operation is implemented:
https://github.com/MarimerLLC/csla/blob/master/Samples/ProjectTracker/ProjectTracker.DalEf/ProjectDal.cs#L82
public void Insert(ProjectDto item)
{
using (var ctx = ObjectContextManager<PTrackerEntities>.GetManager("PTrackerEntities"))
{
var newItem = new Project
{
Name = item.Name,
Description = item.Description,
Started = item.Started,
Ended = item.Ended
};
ctx.ObjectContext.AddToProjects(newItem);
ctx.ObjectContext.SaveChanges();
item.Id = newItem.Id;
item.LastChanged = newItem.LastChanged;
}
}

Related

Strange Behaviour moving from firebird sql version 2.5 to version 4

I have a Windows application written in C# using embedded firebird sql version 2.5.5.26952, which I am re-working it to update to embedded firebird sql version 4.0.0.2496. I have update the fdb file to the new version,and all the tables and sprocs, are there. When run a cmd.Fill() command for a selected statement rows are returned, if I do a update for a row in the table, I get the expected results back fine. but If I do a insert nothing is returned, and no errors are thrown, but the data is added to the database. If I run the sproc from the FireRobin application, the data is inserted, and a row is returned, so I'm at a loss to know why it is not working from my C# application. below is slimmed down version of the code.
The 2.5 version is using FirebirdSql.Data.FirebirdClient.4.10.0.0
The 4.0 version is using FirebirdSql.Data.FirebirdClient.9.0.2
using (var cmd = new FbDataAdapter("PROC_UPSERTPEOPLE", _connection)
{
DataTable data = new DataTable();
cmd.SelectCommand.Parameters.Add("SURNAME", FbDbType.Text).Value = item.Surname;
cmd.SelectCommand.Parameters.Add("FORENAMENAME", FbDbType.Text).Value = item.Forename);
var transaction = _connection.BeginTransaction();
cmd.SelectCommand.CommandType = CommandType.StoredProcedure;
cmd.SelectCommand.Transaction = transaction;
var result = cmd.Fill(data);
transaction.Commit();
}
On a update result contains 1, and data has the expected result, but on a insert result = 0, and data has no rows in.
Any help would be appreciated.
This is the simple version fo the sproc in question
CREATE OR ALTER PROCEDURE PROC_UPSERTPEOPLE_SLIM
(
RECID INTEGER,
SURNAME VARCHAR(100),
FORENAME VARCHAR(100)
)
RETURNS
(
ID INTEGER,
LSURNAME VARCHAR(100),
LFORENAME VARCHAR(100)
)
AS
DECLARE VARIABLE local_id integer;
DECLARE VARIABLE local_surname varchar(100);
DECLARE VARIABLE local_forename varchar(100);
BEGIN
select
ID,
FORENAME,
SURNAME
FROM
APA_PEOPLE
WHERE
(:RECID IS NOT NULL AND ID = :RECID)
OR (:RECID IS NULL
AND FORENAME = :FORENAME
AND SURNAME = :SURNAME)
INTO
:local_id,
:local_forename,
:local_surname;
IF (:local_id IS NULL) then
begin
UPDATE OR INSERT INTO APA_PEOPLE(FORENAME, SURNAME)
VALUES(:FORENAME, :SURNAME)
MATCHING (FORENAME, SURNAME);
end
else
begin
UPDATE APA_PEOPLE SET FORENAME = :FORENAME,
SURNAME = :SURNAME
WHERE ID = :local_id;
end
FOR
SELECT
ID,
SURNAME,
FORENAME
from
APA_PEOPLE
WHERE
(:RECID IS NOT NULL AND ID = :RECID)
OR (:RECID IS NULL
AND FORENAME = :FORENAME
AND SURNAME = :SURNAME)
INTO
:ID,
:LSURNAME,
:LFORENAME
DO
begin
suspend;
end
END;
Update
To answer my own question, being mainly a TSQL developer, DSQL seems strange, change the sproc to the following, which is simpler
CREATE OR ALTER PROCEDURE PROC_UPSERTPEOPLE_SLIM (
RECID integer,
SURNAME varchar(100),
FORENAME varchar(100)
)
RETURNS (ID integer)SQL SECURITY INVOKER
AS
BEGIN
UPDATE OR INSERT INTO APA_PEOPLE(FORENAME, SURNAME)
VALUES(:FORENAME, :SURNAME)
MATCHING (FORENAME, SURNAME)
RETURNING ID INTO :ID;
END;
but also had to change the way it was called, to use
EXECUTE PROCEDURE PROC_UPSERTPEOPLE_SLIM(#RECID, #SURNAME, #FORENAME)
This does seem counter intuitive, I had assumed a stored procedure was a stored procedure, and there are not two different flavors. Oh well it works now, so move on to getting the rest of the app to work.

How to insert data into multiple related tables in .NET Core API?

I want to insert data into multiple related tables, I searched, but everything I've found is insert data into multiple tables that aren't related. I have two tables User and OrchardTable, both are related by the user Id, so the primary key of the User table is the Id, which is generated as an identity on the database, and the foreign key of the table OrchardTable is the User Id.
This is from my controller
[HttpPost]
public ActionResult post([FromBody] Models.Users u)
{
using (Models.OrchardAdminContext db = new Models.OrchardAdminContext())
{
Models.User oUser = new Models.User();
Models.OrchardTable userOrchar = new Models.OrchardTable();
oUser.Name = u.Name;
oUser.Email = u.Email;
db.User.Add(oUser);
db.SaveChanges();
var idUser = db.OrchardTable.Where(x => x.email = oUser.Email).FirstOrDefault();
userOrchar.IdUser = idUser.Id;
userOrchar.orchardUbication = u.ubication;
db.OrchardTable.Add(userOrchard);
db.SaveChanges();
}
return Ok();
}
So, the code above do: first adds an user to the User table, on the SQL Server Management Studio the Id of each user is an identity therefore when the data is saved the Id is generated and through the mail the id of the user is recovered to make the relation between the tables User and OrchardTable by the Id.
I think this is a way of do it, but not the best. Besides the main question, I want to know if there are a better and correct way to do the insert.
Any help is appreciated.
UPDATE
Stored procedure to insert into the table User and return the Id from the same instert:
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE spUserInsert
#name varchar(20),
#email varchar(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
insert into user(name, email) values (#name, #email);
DECLARE #Id INT
SET #Id=##IDENTITY
SELECT #Id AS id
END
GO
So if I'm not mistaken, the idea is to create an SP to insert and return the id of that insert, then with the id send it to another SP where it is inserted into OrchardTable. But now when I call it from the controller it doesn't seem to work because no saved record shows up in the database.
The controller:
[HttpPost]
public ActionResult post([FromBody] Models.Users u)
{
using (Models.OrchardAdminContext db = new Models.OrchardAdminContext())
{
Models.User oUser = new Models.User();
Models.OrchardTable userOrchar = new Models.OrchardTable();
oUsuar.Name = u.Name;
oUsuar.Email = u.Email;
var idUserReg = db.User.FromSqlRaw("Execute spUserInsert #name, #email", oUsuar.Name, oUsuar.Email);
db.SaveChanges();
}
return Ok();
}
I tested the stored procedure on Sql Server and works, but on the controller not, and I don't know why on the controller din't work.
Again, any help is appreciated.
With respect to the procedure to encapsulate the insert, this is what I had in mind you could do
create procedure UserInsert
#Name varchar(20),
#Email varchar(100)
<#anyOtherParameters>
as
set nocount, xact_abort on
declare #Id int
begin tran
insert into [user]([name], email)
values (#Name, #Email);
set #Id=Scope_Identity();
insert into OrchardTable (Id, <othercolumns?>)
values (#Id, #OtherParams?);
commit tran
go
You only need to make one call from code, the procedure will insert the user, get the identity and then insert this Id into the OrchardTable
If you need to include any other data just pass as paramters also and use for the insert - if you only want a row with the corresponding ID then just leave as a single insert for the Id.
Try this:
var pName = new SqlParameter("#name", oUsuar.Name);
var pEmail = new SqlParameter("#email", oUsuar.Email);
var idUserReg = db.DataBase.ExecuteSqlCommand("Exec spUserInsert #name, #email",
new[] { pName, pEmail });

Update multiple columns after insert using Entity Framework

I've created a database trigger on my table that updates a field in the table after an insert. When doing an insert using EF I'll get the ID and the number. On the database I've created this code:
create table Things (
ID int primary key identity not null,
Number nvarchar(20)
);
create trigger UpdateThingsNumberTrigger on Things
after insert
as
begin
declare #month nvarchar(2);
select #month = cast(month(getdate()) as nvarchar(2));
declare #code nvarchar(15);
select #code = cast(year(getdate()) as nvarchar(4)) +
'.' +
replicate('0', 2 - len(#month)) +
#month +
'.';
declare #max nvarchar(20);
select #max = t.ID
from Things t
where ID like #code + '%';
with CTE_UPD as
(
select
replicate('0',
4 -
len(cast(coalesce(cast(right(#max, 4) as int), 0) + row_number() over (order by ins.ID) as nvarchar(4)))) +
cast(coalesce(cast(right(#max, 4) as int), 0) + row_number() over (order by ins.ID) as nvarchar(4)) as NextNo,
ID
from Things ins
)
update Things
set Number = #code + NextNo
from Things t inner join CTE_UPD ins on ins.ID = t.ID;
end
Note: For the logical flaw inside the trigger, I'll refer to Create an incremental number with year and month without updating the entire table using a trigger on Database Administrators SE.
This part of my code works fine, ignoring the logical flaw inside the trigger… The problem I'll try to solve in this question, is when I insert a thing in my table from Entity Framework (database first). There is my code and the output:
using (Database db = new Database())
{
Thing thing = new Thing(); // --> just an empty constructor.
db.Entry(thing).State = EntityState.Added;
await db.SaveChangesAsync();
Console.WriteLine($"ID = {thing.ID}");
Console.WriteLine($"Number = {thing.Number}");
}
// Output:
// ID = 1
// Number =
In the background EF is doing this code on the server when calling SaveChangesAsync():
INSERT [dbo].[Things]([Number])
VALUES (NULL)
SELECT [ID]
FROM [dbo].[Things]
WHERE ##ROWCOUNT > 0 AND [ID] = scope_identity()
Now can EF update the ID in the C# object. But how could I get the number without using code below before closing the using block?
Thing recentlyInsertedThing = await db.Things.FindAsync(thing.ID);
I've found it to get the ID and the Number without writing a 2nd select statement. This is my code:
using (Database db = new Database())
{
Thing thing = new Thing();
string sql = #"insert into Things()
values ();
select ID, Number
from Things
where ##rowcount > 0 and ID = scope_identity();";
KeyMapper recentlyRecevedKeys = await db
.Database
.SqlQuery<KeyMapper>(sql)
.FirstAsync();
thing.ID = recentlyRecevedKeys.ID;
thing.Number = recentlyRecevedKeys.Number;
}
// Nested class
private class KeyMapper
{
public int ID { get; set; }
public string Number { get; set; }
}

Use stored procedure with parameters in Entity Framework

I'm using Entity Framework 6.1.3 and have used the database-first approach to let it generate the model files and the .EDMX. I also have the following stored procedure on SQL Server 2008 R2, which Entity Framework brought into the EDMX:
CREATE PROCEDURE [dbo].[FindEmployee]
#LastName nvarchar(50),
#employeeID nvarchar(50),
#securityID nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
select *
from Employee
where
(lastName = dbo.trim(#LastName) AND dbo.trim(#LastName) IS NOT NULL)
OR (employeeID = dbo.trim(#employeeID) AND dbo.trim(#employeeID) IS NOT NULL)
OR (securityID = dbo.trim(#securityID) AND dbo.trim(#securityID) IS NOT NULL)
order by
case when dbo.trim(#LastName) is not null then CONVERT(char(50), lastName) + CONVERT(char(50), firstName)
when dbo.trim(#employeeID) is not null then employeeID
when dbo.trim(#securityID) is not null then securityID
end
END
In a Windows WPF app, I let the user select the column to search on (lastName, employeeID, or securityID) from a combobox. The user provides a search value which will get plugged into that parameter in the call to the stored procedure. The stored procedure then returns a resultset from its SELECT which I'll use to populate a DataGrid.
I'm trying to call the stored procedure in this code; Note that the FindEmployee_Result is an auto-generated class in the EDMX for the stored procedure:
public FindEmployee_Result SearchEmployees(string lastName, string employeeID, string securityID)
{
var results = new FindEmployee_Result();
using (var ctx = new TestSelectionEntities())
{
results = ctx.FindEmployee(lastName,employeeID,securityID);
}
return results;
}
This code blows up with this error:
Cannot implicitly convert type 'System.Data.Entity.Core.Objects.ObjectResult' to 'TestSelection.data.FindEmployee_Result'
What am I missing? Thank you.
The solution is to use a List, as the stored procedure returns a resultset of FindEmployee_Result objects:
public List<FindEmployee_Result> SearchEmployees(string lastName, string employeeID, string securityID)
{
using (var ctx = new TestSelectionEntities())
{
return ctx.FindEmployee(lastName,employeeID,securityID).ToList();
}
}

How to write this stored procedure

I have a table Student. This table has a column ID which is auto-increment. I want to write a stored procedure to add a new student and return the ID. I'm newbie at this.
This is my code. Please check if it's wrong and fix for me and show me how to code to use it in C#. I used Entity Framework 4.
#Name NVARCHAR(50),
#Birthday DATETIME,
#ID bigint OUTPUT
AS
BEGIN
SET XACT_ABORT ON
BEGIN
INSERT INTO [Student](Name, Birthday) VALUES (#Name, #Birthday);
SELECT #ID = SCOPE_IDENTITY()
END
END
It is better you can C# code instead SP when your working with EF4.
Using Entity Framework, this is all done automagically for you.
using (MyEntities context = new MyEntities())
{
var student = new Student()
{
Name = "some value",
Birthday = "some Birthday"
};
context.Students.AddObject(student);
context.SaveChanges();
int ID = student.ID; // You will get here the Auto-Incremented table ID value.
}
Saving in Entity Framework will automatically update the "auto-increment" ID column. You just read it out after the call to .SaveChanges();
EDIT:
Also read this post if you encounter any issues getting the "auto-increment" ID value.
#Name NVARCHAR(50),
#Birthday DATETIME,
#ID bigint OUTPUT
AS
BEGIN
SET XACT_ABORT ON
BEGIN
INSERT INTO [Student](Name,Birthday)
VALUES(#Name,#Birthday);
SELECT #ID = SCOPE_IDENTITY()
END
I just added commas in between fields
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx
This should provide you all the information you need to build a C# application calling your Stored Procedure.

Categories

Resources