EF4 - update [Table] set #p = 0 where - entity-framework

While going through SQL profiler, I noticed the following query generated by EF4.
exec sp_executesql N'declare #p int
update [dbo].[User]
set #p = 0
where (([UserID] = #0) and ([RowVersion] = #1))
select [RowVersion]
from [dbo].[User]
where ##ROWCOUNT > 0 and [UserID] = #0',N'#0 int,#1 binary(8)',#0=1,#1=0x000000000042DDCD
I am not sure why EF4 generates this while I am actually not updating any columns of the User table in that UnitOfWork. Running this query updates the RowVersion column (timestamp datatype) which leads to OptimisticConcurrencyException in the next UnitOfWork.
A quick googling led me to this link, which confirms that others have also run into this scenario without finding a solution yet.
Would greatly appreciate any pointers.
Edit: A sample code to replicate the issue.
User and Session tables have a foreign key relationship. Also, in EF4 I have set the "Concurrency Mode" property of RowVersion columns of both entities to Fixed.
Below is a sample method to replicate the scenario.
private static void UpdateSession()
{
using (var context = new TestEntities())
{
context.ContextOptions.ProxyCreationEnabled = false;
var session = context.Users.Include("Sessions").First().Sessions.First();
session.LastActivityTime = DateTime.Now;
context.ApplyCurrentValues("Sessions", session);
context.SaveChanges();
}
}
I see from Sql profiler the following queries being genrated by EF4.
exec sp_executesql N'update [dbo].[Session]
set [LastActivityTime] = #0
where (([SessionID] = #1) and ([RowVersion] = #2))
select [RowVersion]
from [dbo].[Session]
where ##ROWCOUNT > 0 and [SessionID] = #1',N'#0 datetime2(7),#1 int,#2 binary(8)',#0='2011-06-20 09:43:30.6919628',#1=1,#2=0x00000000000007D7
And the next query is weird.
exec sp_executesql N'declare #p int
update [dbo].[User]
set #p = 0
where (([UserID] = #0) and ([RowVersion] = #1))
select [RowVersion]
from [dbo].[User]
where ##ROWCOUNT > 0 and [UserID] = #0',N'#0 int,#1 binary(8)',#0=1,#1=0x00000000000007D3

Not sure if this is still problem for you but here is the hotfix by MS http://support.microsoft.com/kb/2390624

Found this link referenced on another forum and was able to obtain a download for the hotfix mentioned by Kris Ivanov.
http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=2390624

Not sure about EF4, but with 4.1 we turned off the rowversion/timestamp by setting it to concurrenttoken = false.
We did this because
It was a calculated field in our db
It should never be changed by the application (in our case)

Related

Unable to update rows individually

I am trying to update our patient table within our system, but due to a connection we have with the state that sends modifications to that table to an external system I'm unable to do mass updates due to several triggers in our system (Microsoft SQL Server 2008 R2). The main piece of code in the trigger that is preventing
IF (#numrows > 1)
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION
SELECT #errmsg = OBJECT_NAME(##PROCID) + ' : more than one row is updated in table Patient'
RAISERROR(#errmsg,16,21)
RETURN
END
I cannot simply shut these triggers off where it would break a lot of things, so to do what would be a simple act of
update patient set security_level = '2'
where security_level = '1'
I used the following code which used to work in prior versions
declare #tmp_table table(
PRG int identity(1,1) Primary Key,
patient_id int
)
declare #start_value int = 1,
#finish_value int,
#patient_id int
Insert Into #tmp_table(patient_id ) Select patient_id From patient where security_level = '1'
Select #finish_value = max(PRG) From #tmp_table
While #start_value <= #finish_value
Begin
--now get a key for patient and store in variables
Select #patient_id = patient_id
From #tmp_table
Where PRG = #start_value
--.. and now update by key
Update patient
set security_level = '2'
Where patient_id = #patient_id
Set #start_value = #start_value + 1
End
I get the following error upon running that code
Msg 50000, Level 16, State 21, Procedure tU_Patient_HL7, Line 64
tU_Patient_HL7 : more than one row is updated in table Patient
Msg 3609, Level 16, State 1, Line 22
The transaction ended in the trigger. The batch has been aborted.
Any idea how I could tweak this or recode this to update the security level of all patients who have a security level set at 1 and switch it to 2?
Update
Would there be any way I could loop
Update top (1) patient
set security_level = '2'
where security_level = '1'
Until all rows are affected? That would work as well.
With out full code ,it is hard .I am guessing your update statement is conflicting with below peice of code..
IF (#numrows > 1)
even though you use
Update patient
set security_level = '2'
Where patient_id = #patient_id
Your update query may affect more than one row.So my best bet would be to tweak your update query to below or change trigger if you can which is desirable
Update top (1) patient
set security_level = '2'
Where patient_id = #patient_id
If you add to your update code a "GO 100", that should do the trick. You may need to put a delay in there as well if it updates too fast for the trigger.
Update top (1) patient
set security_level = '2'
where security_level = '1'
GO 100
The number of executions (i.e. 100 in my example) is up to you.

How to set a bit based on a value existing in a table

I have a table. I have 2 variables, one is a bit, the other is an int.
Table: WorkGroupCollectionDetail
Variables: #WorkgroupID int, #IsFSBP bit
The table has WorkGroupId int PK and WorkGroupCollectionCode varchar PK. That's it.
I can run a query like this:
SELECT WorkGroupId
FROM WorkGroupCollectionDetail
WHERE WorkGroupCollectionCode = 'FSBP'
and it gives me a list of WorkGroupID.
So what I need to do is if the value of #WorkgroupID is inside the results of that query, I need to set the bit variable to true.
select #IsFBSP = case
when exists (
select 42 from WorkGroupDetailCollection
where WorkGroupCollectionCode = 'FSBP' and WorkGroupId = #WorkGroupId ) then 1
else 0 end
which is logically equivalent to:
select #IsFBSP = case
when #WorkGroupId in (
select WorkGroupId from WorkGroupDetailCollection
where WorkGroupCollectionCode = 'FSBP' ) then 1
else 0 end
A query using EXISTS often performs better than a query using IN. You can check the execution plans to see how they compare in your particular case.
Note that these examples include setting the bit value to zero as well as one.
You could modify the SELECT to include the check for the WorkGroupId and update the #IsFSBP accordingly:
IF EXISTS(SELECT WorkGroupId
FROM WorkGroupCollectionDetail
WHERE WorkGroupCollectionCode = 'FSBP'
AND WorkGroupId = #WorkgroupID)
BEGIN
SELECT #IsFSBP = 1;
END
SQL Fiddle example
I'm guessing you're looking for
Set #BitVariable = count(*)
From TestTable
WHERE TestCode = 'TestValue' and TestID = #TestID

Entity Framework executes both INSERT and UPDATE stored procedures when you do INSERT

I have this stored procedure, which is mapped in an Entity Framework 4.1 object. The call is made within the
using (TransactionScope transaction = new TransactionScope())
{
try
{
DbEntity.Car.AddObject(CarInfo);
DbEntity.SaveChanges();
/* Other object savings */
transaction.Complete();
DbEntity.AcceptAllChanges();
}
catch (Exception exp)
{
throw exp;
}
finally
{
DbEntity.Dispose();
}
}
I see the stored procedure mapping done currently. If I execute the stored procedure alone on MS SQL server, it executes it correctly.
Here is the stored procedure
ALTER PROCEDURE [dbo].[Carinsert] #Qty INT
,#StyleID INT
,#TFee MONEY
,#HWayTax MONEY
,#OFees MONEY
,#OFeesDescription NTEXT
,#MUp DECIMAL(18, 4)
,#BAss MONEY
,#PriceMSRP MONEY
,#PriceSpecial MONEY
AS
BEGIN
SET nocount ON
DECLARE #PTotal MONEY
DECLARE #TaxFeesNet MONEY
DECLARE #CarID INT
SET #TaxFeesNet = Isnull(#TFee, 0) + Isnull(#HWayTax, 0)
+ Isnull(#OFees, 0)
IF( #PriceSpecial IS NULL )
BEGIN
SET #PTotal = #PriceMSRP + #TaxFeesNet
END
ELSE
BEGIN
SET #PTotal = #PriceSpecial + #TaxFeesNet
END
INSERT INTO Car
(Qty
,StyleID
,MUp
,BAss
,PriceMSRP
,PriceSpecial
,TFee
,HWayTax
,OFees
,OFeesDescription
,PriceTotal)
VALUES (#Qty
,#StyleID
,#MUp
,#BAss
,#PriceMSRP
,#PriceSpecial
,#TFee
,#HWayTax
,#OFees
,#OFeesDescription
,#PTotal)
SELECT Scope_identity() AS CarID
END
If I execute this like on MS SQL it calculates the PriceTotal column in the table as 3444.00, which is correct.
#Qty= 5,
#StyleID = 331410,
#TFee = NULL,
#HWayTax = NULL,
#OFees = NULL,
#OFeesDescription = NULL,
#MUp = 4,
#BAss = 10000,
#PriceMSRP = 20120,
#PriceSpecial = 3444
When I run the MVC web application, and I debug & see these are the values passed and the PriceTotal comes to 20120.00
I couldn't figure out why it does not do the IF ELSE calculation & use the price.
Does anybody else see something weird? This has been daunting for few days now. Any help appreciated. Thanks
Update
I updated the title to better guide others
After few minutes of posting the question, I figured it all out.
There was actually 2 bug.
Entity framework used the UPDATE stored procedure even when I try to INSERT new records. Just for records, the UPDATE stored procedure required the CarID [primary key]. May be EF first does the INSERT and then does the UPDATE right away, even for creating new records?
The UPDATE sproc had a bug checking for NULL using <>. It should have been IS NOT NULL
In anycase, now This is a bigger issue. How to force EF to use INSERT sproc and not the UPDATE when I only want to do is create a new record?
I tried
DbEntityContext.ObjectStateManager.ChangeObjectState(carInfo, EntityState.Added);
and still EF kept on calling the UPDATE sproc.

Resolve concurency during updating table in Sybase

I have a procedure in Sybase with the following code.
begin transaction get_virtual_acc
UPDATE store_virtual_acc SET isProc = 1, Uid = #uid, DateReserv = getdate()
from store_virtual_acc (index idx_id) WHERE id = (SELECT min(id) FROM store_virtual_acc (index idx_uid) where Uid = null and isProc = null)
commit transaction get_virtual_acc
The problem is that when the procedure is called multiple users concurently they can receive the same min(id) and update same row in the table with different value #uid. The result is a distortion of the data. It is necessary to achieve a result, that if the line has already selected for update a single user, the other can't select it. Table have lock type datarows.
Tried to use a transaction-level locking as follows
set transaction isolation level 3
before the transaction begin but aplication wich call the procedure get exception
java.sql.SQLException: Your server command (family id # 0, process id # 530) encountered a deadlock situation. Please re-run your command.
I would be grateful for any help.
Try something like this:
begin transaction get_virtual_acc
UPDATE store_virtual_acc SET isProc = 1, Uid = #uid, DateReserv = getdate()
from store_virtual_acc (index idx_id) WHERE id = (SELECT min(id) FROM store_virtual_acc (index idx_uid) holdlock where Uid = null and isProc = null )
commit transaction get_virtual_acc
The keyword is holdlock

Metadata about a column in SQL Server 2008 R2?

I'm trying to figure out a way to store metadata about a column without repeating myself.
I'm currently working on a generic dimension loading SSIS package that will handle all my dimensions. It currently does :
Create a temporary table identical to the given table name in parameters (this is a generic stored procedure that receive the table name as parameter, and then do : select top 0 * into ##[INSERT ORIGINAL TABLE NAME HERE] from [INSERT ORIGINAL TABLE NAME HERE]).
==> Here we insert custom code for this particular dimension that will first query the data from a datasource and get my delta, then transform the data and finally loads it into my temporary table.
Merge the temporary table into my original table with a T-SQL MERGE, taking care of type1 and type2 fields accordingly.
My problem right now is that I have to maintain a table with all the fields in it to store a metadata to tell my scripts if this particular field is type1 or type2... this is nonsense, I can get the same data (minus type1/type2) from sys.columns/sys.types.
I was ultimately thinking about renaming my fields to include their type in it, such as :
FirstName_T2, LastName_T2, Sex_T1 (well, I know this can be type2, let's not fall into that debate here).
What do you guyz would do with that? My solution (using a table with that metadata) is currently in place and working, but it's obvious that repeating myself from the systables to a custom table is nonsense, just for a simple type1/type2 info.
UPDATE: I also thought about creating user defined types like varchar => t1_varchar, t2_varchar, etc. This sounds like something a bit sluggy too...
Everything you need should already be in INFORMATION_SCHEMA.COLUMNS
I can't follow your thinking of not using provided tables/views...
Edit: As scarpacci mentioned, this somewhat portable if needed.
I know this is bad, but I will post an answer to my own question... Thanks to GBN for the help tho!
I am now storing "flags" in the "description" field of my columns. I, for example, can store a flag this way : "TYPE_2_DATA".
Then, I use this query to get the flag back for each and every column :
select columns.name as [column_name]
,types.name as [type_name]
,extended_properties.value as [column_flags]
from sys.columns
inner join sys.types
on columns.system_type_id = types.system_type_id
left join sys.extended_properties
on extended_properties.major_id = columns.object_id
and extended_properties.minor_id = columns.column_id
and extended_properties.name = 'MS_Description'
where object_id = ( select id from sys.sysobjects where name = 'DimDivision' )
and is_identity = 0
order by column_id
Now I can store metadata about columns without having to create a separate table. I use what's already in place and I don't repeat myself. I'm not sure this is the best possible solution yet, but it works and is far better than duplicating information.
In the future, I will be able to use this field to store more metadata, where as : "TYPE_2_DATA|ANOTHER_FLAG|ETC|OH BOY!".
UPDATE :
I now store the information in separate extended properties. You can manage extended properties using sp_addextendedproperty and sp_updateextendedproperty stored procedures. I have created a simple store procedure that help me to update those values regardless if they currently exist or not :
create procedure [dbo].[UpdateSCDType]
#tablename nvarchar(50),
#fieldname nvarchar(50),
#scdtype char(1),
#dbschema nvarchar(25) = 'dbo'
as
begin
declare #already_exists int;
if ( #scdtype = '1' or #scdtype = '2' )
begin
select #already_exists = count(1)
from sys.columns
inner join sys.extended_properties
on extended_properties.major_id = columns.object_id
and extended_properties.minor_id = columns.column_id
and extended_properties.name = 'ScdType'
where object_id = (select sysobjects.id from sys.sysobjects where sysobjects.name = #tablename)
and columns.name = #fieldname
if ( #already_exists = 0 )
begin
exec sys.sp_addextendedproperty
#name = N'Scd_Type',
#value = #scdtype,
#level0type = N'SCHEMA',
#level0name = #dbschema,
#level1type = N'TABLE',
#level1name = #tablename,
#level2type = N'COLUMN',
#level2name = #fieldname
end
else
begin
exec sys.sp_updateextendedproperty
#name = N'Scd_Type',
#value = #scdtype,
#level0type = N'SCHEMA',
#level0name = #dbschema,
#level1type = N'TABLE',
#level1name = #tablename,
#level2type = N'COLUMN',
#level2name = #fieldname
end
end
end
Thanks again