All our CRUD operations have a CommitTransaction() at the end before returning the results of a Method. For instance, we have a method Create() that creates a record if there is no existing record with a Status: 'Float'. Otherwise, it selects the record and updates its contents.
This is working as checked in the Database. The problem is, the method is returning its previous data and not the updated one:
public class Sample
{
public int ID {get; set;}
public string Code {get;set;}
public decimal Total {get; set;}
}
In Create(), Code and Total is entered by the user which should update the data in the database.
E.g.
ID | Code | Total
1 | CodeName | 100
The user updates
Code : 'Code1'
Total : 200
But the Method still returns
Code : 'CodeName '
Total : 100
But if checked in the Database, it is already
ID | Code | Total
1 | Code1 | 200
I have observed that this only happens when a CommitTransaction() is used which is not advisable to remove but a correct data return is also needed.
Here is a simplified replica of Create():
private string procCRUD = "procCreate #ID={0},#Code={1},#Total={2}";
public Sample Create(Sample data)
{
IQueryable<ComponentType> query = contextBase.Samples.FromSql(procCRUD,"create",data.ID, data.Code, data.Total); // contextBase.Samples is the DbContext
commit(); // this commits the transaction
return query;
}
procCRUD is a stored procedure:
DROP PROCEDURE IF EXISTS procCreate
GO
CREATE PROCEDURE procCreate
#proc varchar(20) = 'create',
#ID bigint = 0,
#Code varchar(20)= NULL,
#Total int= 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL varchar(MAX)
SET #SQL = ''
IF (#proc='create') BEGIN
IF (NOT EXISTS(SELECT ID FROM Sample WHERE Status='Float'))
BEGIN
INSERT INTO Sample(Code, Total) VALUES( #Code, #Total)
SET #ID = ##IDENTITY;
END
ELSE
BEGIN
SELECT #ID=ID FROM Sample WHERE Status='Float'
UPDATE Sample SET
Code = #Code
,Total = #Total
WHERE ID=#ID
END
END
SET #SQL = 'SELECT TOP 1 * FROM Sample '
EXEC SP_EXECUTESQL #SQL
END
GO
I have tried manually setting the return value e.g. query.Code = data.Code before the return but it is time-consuming if I have more than 10 Class properties. Also, some properties are not entered by the user but retrieved from the database depends on another property entered.
I wonder if some of you also encountered this problem. Any help will be appreciated. Thank you.
(edited)
You need to better explain what you are trying to do and what is the actual code behind your
contextBase.Samples.FromSql
At least, the following seems to be relevant, based on your (not complete) description.
Copying from base to parent.
I have tried manually setting the return value e.g. query.Code = data.Code before the return but it is time-consuming if I have more than 10 Class properties.
Please read the solution already suggested here.
You will end up with defining a Map extension and using
query.Map(data);
The above should resolve your actual problem, but there are other inconsistencies in your question, that might affect the result.
SQL
procCRUD is a stored procedure:
SET #SQL = 'SELECT TOP 1 * FROM Sample '
EXEC SP_EXECUTESQL #SQL
END
GO
In the end of your stored procedure you are managing a select to return the correct value, but - as far as I can see - you are missing the correct where condition, something like a WHERE ID=#ID (or Status='Float', I don't know your app logic).
By the way, you should only Commit() after a BeginTransaction, but that is not shown in your question.
Nothing in your description really makes sense. According to you, you:
do a select
commit a transaction
return the values
Wonder that the value returned and the one in the Database do not match?
If you want to retreive values as a Update or Insert operation is running, the only reliable syntax in SQL is OUTPUT: https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql
Returns information from, or expressions based on, each row affected by an INSERT, UPDATE, DELETE, or MERGE statement. These results can be returned to the processing application for use in such things as confirmation messages, archiving, and other such application requirements. The results can also be inserted into a table or table variable. Additionally, you can capture the results of an OUTPUT clause in a nested INSERT, UPDATE, DELETE, or MERGE statement, and insert those results into a target table or view.
If you - for example - want to get teh Primary Key of something you just inserted, OUTPUT Inserted.ID is the only way that will reliably work. Unfortunatily, most non-MSSQL DBMS do not have such a feature.
Related
Before you see the code, I want to try and explain what I want to happen. This is a personal project, not worried about sql injection, nothing production, etc., etc..
My application lets a user create an account, select which classes they've taken, then views them.
What I have currently: A user creates a username/password that is stored in a Users table. The username is also stored as a global variable. At the same time, a copy of the master course list (107 rows) is made and renamed as the users username (from the global variable.) After creating the username/password, the user is brought to a form where they select courses they've taken and their corresponding grade.
Where I'm stuck: The user can select which courses they've taken and choose their corresponding grade. The users table is made on the fly so I don't know any other way to access the table during run time besides using Dynamic SQL. I just want to update the user table with their selection during run-time.
From here, my code basically says "If check marked, update users course table with their grade and status where course# = x." Is my issue the global variable? Should I use my Users table somehow? Should I use QUOTENAME in some way instead of UPDATE? My code with #sql keeps coming up with syntax errors so I feel like a simple fix could do the trick.
private void btnNext_Click(object sender, EventArgs e)
{
if (checkIntrotoPublicSpeaking.Checked || checkEffectiveOralCommunication.Checked || checkProfComm.Checked)
{
List<SqlParameter> sqlOralComm = new List<SqlParameter>();
sqlOralComm.Add(new SqlParameter("Username", GlobalVariables.username));
sqlOralComm.Add(new SqlParameter("IntrotoPublicSpeaking", cboxIntrotoPublicSpeaking.Text));
sqlOralComm.Add(new SqlParameter("EffectiveOralCommunication", cboxEffectiveOralCommunication.Text));
sqlOralComm.Add(new SqlParameter("ProfComm", cboxProfComm.Text));
DAL.ExecSP("CreateOralComm", sqlOralComm);
}
}
and
ALTER PROCEDURE [dbo].[CreateOralComm]
-- Add the parameters for the stored procedure here
#Username nvarchar(30),
#IntrotoPublicSpeaking nvarchar(3),
#EffectiveOralCommunication nvarchar(3),
#ProfComm nvarchar(3)
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 #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ', Status = ''Completed'' WHERE Course =
7600105'
EXEC sp_executesql #sql;
/*
SELECT QUOTENAME(#Username)
SET Grade = 'A', Status = 'Completed'
WHERE Course = 7600105;
*/
END
GO
I feel like a nuisance. I know I'm not that good and there's probably A LOT of easier ways to go about this but I'm really trying my best to get through this with the knowledge I have.
SQL Server 2016 accessed by ASP.NET 4.6.2 MVC web application
I have table "Building" and a Building can have multiple "Components". For example, Building1 has Component1 and Component2 ... etc
It was requested of me to be able to lock a building. Lock means that a component can no longer be modified (CREATE/UPDATE/DELETE). Well, as you can imagine, this is a huge application and a component can be modified in 100+ places. No one can even answer the question, "Where all do I need to lock?".
My thought is to lock everywhere I can think of and then as a safety net create a SQL Trigger that prevents all modifications if the column on the Component table "IsLocked BIT" is true. Currently, the only way I know if a component is locked is if the IsLocked column equals true.
So, I say all of that for this. How do I create a SQL Server Trigger that prevents a row of data from being modified if the row being modified has column IsLocked = 1?
Edit 1
In my opinion, this is not a duplicate. Using Instead of Delete or Instead of... will not work for me. If I do the instead of ... then inside of that I will need to provide commit logic. I don't want to provide commit logic. I just want to run a check prior to insert, update, delete.
Edit 2 - Instead of Update/Delete is best choice
If instead of... is my best choice than can someone rewrite what I have using the instead of update/delete? I don't know how to do it. Please keep in mind that requests will be coming from a web app. I won't know if they are updating one column or the entire entity or what they will passing in. I know that the way I have it written that it will catch any insert/update/delete and prevent it if locked. If there is a better way then please write it and explain why it is better.
Here is the solution I came up with:
ALTER TRIGGER [dbo].[PreventLockedModification]
ON [dbo].[Component]
FOR INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
--DETERMINE INSERT(I) UPDATE(U) OR DELETE(D)
----------------------------------------------------------------------------------------------------
DECLARE #action as char(1);
SET #action = 'I'; -- Set Action to Insert by default.
IF EXISTS(SELECT * FROM DELETED)
BEGIN
SET #action =
CASE
WHEN EXISTS(SELECT * FROM INSERTED) THEN 'U' -- Set Action to Updated.
ELSE 'D' -- Set Action to Deleted.
END
END
----------------------------------------------------------------------------------------------------
DECLARE #ErrorMsg nvarchar(100) = 'This row is locked and cannot be updated';
DECLARE #IsLocked bit;
DECLARE #BuildingId bigint;
DECLARE #UnitId bigint;
DECLARE #IsComplete_Building bit;
DECLARE #IsComplete_Unit bit;
----------------------------------------------------------------------------------------------------
IF #action = 'U' or #action = 'D'
BEGIN
SELECT #IsLocked = IsLocked FROM deleted;
IF #IsLocked = 1
BEGIN
RAISERROR (#ErrorMsg, 16, 1);
ROLLBACK TRANSACTION
END
END
----------------------------------------------------------------------------------------------------
ELSE IF #action = 'I'
BEGIN
SELECT #BuildingId = BuildingId FROM inserted;
SELECT #UnitId = UnitId FROM inserted;
SELECT #IsComplete_Building = IsComplete FROM Building WHERE BuildingId = #BuildingId
SELECT #IsComplete_Unit = IsComplete FROM Unit WHERE UnitId = #UnitId
IF #IsComplete_Building = 1 or #IsComplete_Unit = 1
BEGIN
RAISERROR (#ErrorMsg, 16, 1);
ROLLBACK TRANSACTION
END
END
----------------------------------------------------------------------------------------------------
END
I have the following stored procedure in an SQL Server 2005 database (meant simply to return the database size in MB).
ALTER PROCEDURE [dbo].[dbSize]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sizeMb int
DECLARE #DB_NAME varchar(100)
SELECT #DB_NAME = DB_NAME()
SELECT #sizeMb = (size*8)/1024 FROM sys.master_files
WHERE DB_NAME(database_id) = #DB_NAME
AND Name = #DB_NAME
RETURN #sizeMb
END
When I run this in SQL Server Management Studio, it works correctly, returning the current DB Size in MB.
I want this to run inside an application, so I added it to a linq to sql datacontext, which generated the following code:
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.dbSize")]
public int dbSize()
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
return ((int)(result.ReturnValue));
}
I call it like so:
int dbSize = db.dbSize();
However, it only returns zero, never anything else. No exceptions of any kind are thrown either.
I experimented with selecting a result and using output parameters, but that didn't help either (the output parameter was zero as well). Any suggestions?
I've run into the same problem. As dumb as it sounds, you have to return a single row with single column containing your value from a stored procedure. L2S doesn't do something akin to ExecuteScalar(...).
If you use a UDF, you'll have better luck. This post speaks to the problem nicely.
I have a form which contains a data grid and a save button.
When the user clicks the save button I check for new rows by checking a specific column. If its value is 0 I insert the row to database, and if the column value is not 0 then I update that row.
I can insert correctly but when updating an exception occurs:
ChangeConflictException was unhandled,1 of 6 updates failed.
I have checked the update statement and I'm sure it's correct. What is the problem, can any one help me?
int id;
for (int i = 0; i < dgvInstructores.Rows.Count - 1; i++)
{
id = int.Parse(dgvInstructores.Rows[i].Cells["ID"].Value.toString());
if (id == 0)
{
dataClass.procInsertInstructores(name, nationalNum, tel1, tel2,
address, email);
dataClass.SubmitChanges();
}
else
{
dataClass.procUpdateInstructores(id, name, nationalNum, tel1, tel2,
address, email);
dataClass.SubmitChanges();
}
}
I'm using linq to query sql server2005 database and vs2008
the stored procedure for 'procUpdateInstructores' is :
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER proc [dbo].[procUpdateInstructores]
#ID int,
#name varchar(255),
#NationalNum varchar(25),
#tel1 varchar(15),
#tel2 varchar(15),
#address varchar(255),
#email varchar(255)
as
begin
BEGIN TRANSACTION
update dbo.Instructores
set
Name = #name , NationalNum = #NationalNum ,
tel1 = #tel1 , tel2 = #tel2 , address = #address , email = #email
where ID = #ID
IF (##ROWCOUNT > 0) AND (##ERROR = 0)
BEGIN
COMMIT TRANSACTION
END
ELSE
BEGIN
ROLLBACK TRANSACTION
END
end
In my experience, (working with .net forms and mvc with linq-to-sql) I have found that several times if the form collection contains the ID parameter of the data object then the update surely fails.
Even if the ID is the actual ID, it is still flagged as 'propertyChanged' when you bind it or update it or assign to another variable.
As such can we see the code for your stored procs? More specifically, the update proc?
The code you have posted above is fine, the exception should be coming from your stored proc.
However if you are confident that the proc is correct then perhaps look at the HTML code being used to generate the table. Some bugs might be present with respect to 0/1 on ID columns, etc.
In the absence of further information (what your SQL or C# update code looks like...) my first recommendation would be to do SubmitChanges once, outside the for loop, rather than submitting changes once per row.
It appears in this case that you are using a DataGridView (thus WinForms). I further guess that your dataClass is persisted on the form so that you loaded and bound the DataGridView from the same dataClass that you are trying to save the changes to in this example.
Assuming you are databinding the DataGridView to entities returned via LINQ to SQL, when you edit the values, you are marking the entity in question that it is needing to be updated when the next SubmitChanges is called.
In your update, you are calling dataClass.procUpdateInstructores(id, name, nationalNum, tel1, tel2, address, email); which immediately issues the stored procedure against the database, setting the new values as they have been edited. The next line is the kicker. Since your data context still thinks the object is still dirty, SubmitChanges tries to send another update statement to your database with the original values that it fetched as part of the Where clause (to check for concurrency). Since the stored proc updated those values, the Where clause can't find a matching value and thus returns a concurrency exception.
Your best bet in this case is to modify the LINQ to SQL model to use your stored procedures for updates and inserts rather than the runtime generated versions. Then in your parsing code, simply call SubmitChanges without calling procUpdateInstructores manually. If your dbml is configured correctly, it will call the stored proc rather than the dynamic update statement.
Also, FWIW, your stored proc doesn't seem to be doing anything more than the generated SQL would. Actually, LINQ to SQL would give you more functionality since you aren't doing any concurrency checking in your stored proc anyway. If you are required to use stored procs by your DBA or some security policy, you can retain them, but you may want to consider bypassing them if this is all your stored procs are doing and rely on the runtime generated SQL for updates.
Is there a way to batch stored procedure calls in MySql with the .Net / Connector to increase performance?
Here's the scenario... I'm using a stored procedure that accepts a few parameters as input. This procedure basically checks to see whether an existing record should be updated or a new one inserted (I'm not using INSERT INTO .. ON DUPLICATE KEY UPDATE because the check involves date ranges, so I can't really make a primary key out of the criteria).
I want to call this procedure a lot of times (let's say batches of 1000 or so). I can of course, use one MySqlConnection and one MySqlCommand instance and keep changing the parameter values, and calling .ExecuteNonQuery().
I'm wondering if there's a better way to batch these calls?
The only thought that comes to mind is to manually construct a string like 'call sp_myprocedure(#parama_1,#paramb_1);call sp_myprocedure(#parama_2,#paramb2);...', and then create all the appropriate parameters. I'm not convinced this will be any better than calling .ExecuteNonQuery() a bunch of times.
Any advice? Thanks!
EDIT: More info
I'm actually trying to store data from an external data source, on a regular basis. Basically I'm taking rss feeds of Domain auctions (from various sources like godaddy, pool, etc.), and updating a table with the auction info using this stored procedure (let's call it sp_storeSale). Now, in this table that the sale info gets stored, I want to keep historical records for sales for a given domain, so I have a domain table, and a sale table. The sale table has a many to one relationship with the domain table.
Here's the stored procedure:
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE PROCEDURE `DomainFace`.`sp_storeSale`
(
middle VARCHAR(63),
extension VARCHAR(10),
brokerId INT,
endDate DATETIME,
url VARCHAR(500),
category INT,
saleType INT,
priceOrBid DECIMAL(10, 2),
currency VARCHAR(3)
)
BEGIN
DECLARE existingId BIGINT DEFAULT NULL;
DECLARE domainId BIGINT DEFAULT 0;
SET #domainId = fn_getDomainId(#middle, #extensions);
SET #existingId = (
SELECT id FROM sale
WHERE
domainId = #domainId
AND brokerId = #brokerId
AND UTC_TIMESTAMP() BETWEEN startDate AND endDate
);
IF #existingId IS NOT NULL THEN
UPDATE sale SET
endDate = #endDate,
url = #url,
category = #category,
saleType = #saleType,
priceOrBid = #priceOrBid,
currency = #currency
WHERE
id = #existingId;
ELSE
INSERT INTO sale (domainId, brokerId, startDate, endDate, url,
category, saleType, priceOrBid, currency)
VALUES (#domainId, #brokerId, UTC_TIMESTAMP(), #endDate, #url,
#category, #saleType, #priceOrBid, #currency);
END IF;
END
As you can see, I'm basically looking for an existing record that is not 'expired', but has the same domain, and broker, in which case I assume the auction is not over yet, and the data is an update to the existing auction. Otherwise, I assume the auction is over, it is a historical record, and the data I've got is for a new auction, so I create a new record.
Hope that clears up what I'm trying to achieve :)
I'm not entirely sure what you're trying to do but it sounds kinda house-keeping or maintenance related so I won't be too ashamed at posting the following suggestion.
Why dont you move all of your logic into the database and process it all server side ?
The following example uses a cursor (shock/horror) but it's perfectly acceptable to use them in such circumstances.
If you can avoid using cursors at all - great, but the main point of my suggestion is about moving the logic from your application tier back into the data tier to save on the round trips. You'd call the following sproc once and it would process the entire range of data in single call.
call house_keeping(curdate() - interval 1 month, curdate());
Also, if you can provide just a bit more information about what you're trying to do we might be able to suggest other approaches.
Example stored procedure
drop procedure if exists house_keeping;
delimiter #
create procedure house_keeping
(
in p_start_date date,
in p_end_date date
)
begin
declare v_done tinyint default 0;
declare v_id int unsigned;
declare v_expired_date date;
declare v_cur cursor for
select id, expired_date from foo where
expired_date between p_start_date and p_end_date;
declare continue handler for not found set v_done = 1;
open v_cur;
repeat
fetch v_cur into v_id, v_expired_date;
/*
if <some condition> then
insert ...
else
update ...
end if;
*/
until v_done end repeat;
close v_cur;
end #
delimiter ;
Just incase you think I'm completely mad in suggesting cursors you might want to read this
Optimal MySQL settings for queries that deliver large amounts of data?
Hope this helps :)