how does ADO.NET know that a SQL Server concurrency violation has occurred? - ado.net

I don't understand how ADO.NET recognizes a concurrency violation unless it's doing something beyond what I'm telling it to do, inside its "black box".
My update query in SQL Server 2000 does something like the following example, which is simplified; if the rowversion passed to the stored proc by the client doesn't match the rowversion in the database, the where-clause will fail, and no rows will be updated:
create proc UpdateFoo
#rowversion timestamp OUTPUT,
#id int,
#foodescription varchar(255)
as UPDATE FOO set description = #foodescription
where id = #id and rowversion = #rowversion;
if ##ROWCOUNT = 1
select #rowversion from foo where id = #id;
I create a SqlCommand object and populate the parameters and assign the command object to the SqlDataAdapter's UpdateCommand property. Then I invoke the data adapter's Update method.
There should indeed be a concurrency error because I deliberately change the database row in order to force a new rowversion. But how does ADO.NET know this? Is it doing something more than executing the command?
In the RowUpdated event of the SqlDataAdapter there will be a Concurrency error:
MySqlDataAdapter += (sender, evt) =>
{
if ((evt.Status == UpdateStatus.Continue) && (evt.StatementType == StatementType.Update))
{
// update succeeded
}
else
{
// update failed, check evt.Errors
}
}
Is ADO.NET comparing the rowversions? Is it looking at ##rowcount?

Related

How to avoid JPA persist directly insert into database?

This is the mysql table.
create table Customer
(
id int auto_increment primary key,
birth date null,
createdTime time null,
updateTime datetime(6) null
);
This my java code
#Before
public void init() {
this.entityManagerFactory=Persistence.createEntityManagerFactory("jpaLearn");
this.entityManager=this.entityManagerFactory.createEntityManager();
this.entityTransaction=this.entityManager.getTransaction();
this.entityTransaction.begin();
}
#Test
public void persistentTest() {
this.entityManager.setFlushMode(FlushModeType.COMMIT); //don't work.
for (int i = 0; i < 1000; i++) {
Customer customer = new Customer();
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setUpdateTime(new Date());
this.entityManager.persist(customer);
}
}
#After
public void destroy(){
this.entityTransaction.commit();
this.entityManager.close();
this.entityManagerFactory.close();
}
When I reading the wikibooks of JPA, it said "This means that when you call persist, merge, or remove the database DML INSERT, UPDATE, DELETE is not executed, until commit, or until a flush is triggered."
But at same time my code runing, I read the mysql log, I find each time the persist execution, mysql will execute the sql. And I also read the wireShark, each time will cause the request to Database.
I remember jpa saveAll method can send SQL statements to the database in batches? If I wanna to insert 10000 records, how to improve the efficiency?
My answer below supposes that you use Hibernate as jpa implementation. Hibernate doesn't enable batching by default. This means that it'll send a separate SQL statement for each insert/update operation.
You should set hibernate.jdbc.batch_size property to a number bigger than 0.
It is better to set this property in your persistence.xml file where you have your jpa configuration but since you have not posted it in the question, below it is set directly on the EntityManagerFactory.
#Before
public void init() {
Properties properties = new Properties();
properties.put("hibernate.jdbc.batch_size", "5");
this.entityManagerFactory = Persistence.createEntityManagerFactory("jpaLearn", properties);
this.entityManager = this.entityManagerFactory.createEntityManager();
this.entityTransaction = this.entityManager.getTransaction();
this.entityTransaction.begin();
}
Then by observing your logs you should see that the Customer records are persisted in the database in batches of 5.
For further reading please check: https://www.baeldung.com/jpa-hibernate-batch-insert-update
you should enable batch for hibernate
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_size=20
and use
reWriteBatchedInserts&reWriteBatchedStatements=true
end of your connectionString

Database Won't Update Using Savechanges Method on Entity

I am using the Entity Framework in a MVC Web API and I can't figure out why the changes aren't being saved to the database. I am using the following code:
[HttpPost]
public HttpResponseMessage PostPXE(PXE pxe)
{
var pxeRequestID =
from qsp in db.Queue_Server_Provision
.Where(a => a.Server_Name == pxe.Server_Name)
join isni in db.IaaS_Server_NIC_Information
.Where(a => a.MAC == pxe.Mac_Address)
on qsp.IaaS_ID equals isni.IaaS_Server_Information_IaaS_ID
select qsp.Request_ID;
var QSP = db.Queue_Server_Provision.Find(pxeRequestID.FirstOrDefault());
db.Queue_Server_Provision.Attach(QSP);
QSP.Provision_Status_Code = "240.9";
db.Entry(QSP).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException e)
{
return Request.CreateResponse(HttpStatusCode.NotFound, e.Message);
}
catch (Exception e)
{
return Request.CreateResponse(HttpStatusCode.NotFound, "error");
}
return Request.CreateResponse(HttpStatusCode.OK);
}
While debugging everything seems to work fine until I call the SaveChanges() method, at that point the Provision_Status_Code value reverts to what it was before being modified.
The following SQL is generated from the Entity Framework:
exec sp_executesql
N'update [dbo].[Queue_Server_Provision]
set [Server_Name] = #0,
[Environment_List_ID] = #1,
[Operating_System_List_ID] = #2,
[Container_Size_List_ID] = #3,
[Active_Directory_List_ID] = #4
where ([Request_ID] = #5)
select [IaaS_ID],
[Provision_Status_Code],
[Provision_Status_Text],
[Provision_Phase],
[Last_Update_Time],
[Canceled]
from [dbo].[Queue_Server_Provision]
where ##ROWCOUNT > 0 and [Request_ID] = 5',
N'#0 nvarchar(16),
#1 uniqueidentifier,
#2 uniqueidentifier,
#3 uniqueidentifier,
#4 uniqueidentifier,
#5 uniqueidentifier',
#0=N'IaaSTest222',
#1='31097372-E4A6-461D-AFCC-BFAF069A6710',
#2='CF44FE08-56DE-4A7D-813A-08A7AD215E8B',
#3='15AEB74F-E0CB-4219-AC69-8C623DE8DF46',
#4='EA08BB7E-5C1F-4F6B-83D6-4BF9F6C55FF7',
#5='34CA5F9C-2BF8-40E5-8BDD-5DE7364C18C3'
This looks very much like Entity Framework "thinks" that the Provision_Status_Text column is generated in the database - either because it's an identity (unlikely for a string column) or because it's a computed column.
Indication for this is:
The column is not part of the set expression in the update statement although you changed that column. If the column is database generated this is expected.
The column is returned in the sql select expression. For a database generated column this is expected again because EF wants to update the entity on client side with the current computed database values after an insert or update.
The same applies by the way for the other columns that occur in the select statement.
If you are using Code-First workflow check if those properties are annotated with one of the
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
attributes or if
....HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
....HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
is used with Fluent API.
If you are using Database-First or Model-First workflow with EDMX check your EDMX file for StoreGeneratedPattern="Identity" or StoreGeneratedPattern="Computed" attributes. You should find these settings in the designer surface as well.
It does not necessarily mean that the column is really generated in the database. But if one of these settings is in the model metadata EF will assume it is and behave the way you see it. It could mean that database and model are "out of sync" for some reason because some manual changes have been done in the database schema or the model perhaps without updating the other side.

Entity Framework 4: Selecting Single Record

I'm currently planning on switching my "manual query-writing" code to a nice SQL framework, so I can leave the queries or sql things to the framework, instead of writing the queries myself.
Now I'm wondering how I can get a single record from my table in Entity Framework 4?
I've primarily used SQL like SELECT * FROM {0} WHERE Id = {1}. That doesn't work in EF4, as far as I'm concerned.
Is there a way I can select a single ID-Based record from my Context?
Something like:
public Address GetAddress(int addressId)
{
var result = from Context.Addresses where Address.Id = addressId;
Address adr = result as Address;
return Address;
}
Thank you!
var address = Context.Addresses.First(a => a.Id == addressId);
You can use Single or First methods.
The difference between those methods is that Single expects a single row and throws an exception if it doesn't have a single row.
The usage is the same for both of them
(Based on VS 2015) If you create an .edmx (Add --> ADO.NET Entity Data Model).
Go through the steps to created the ".edmx" and use the following to run the stored procedure. emailAddress is the parameter you are passing to the stored procedure g_getLoginStatus. This will pull the first row into LoginStatus and status is a column in the database:
bool verasity = false;
DBNameEntities db = new DBNameEntities(); // Use name of your DBEntities
var LoginStatus = db.g_getLoginStatus(emailAddress).FirstOrDefault();
if ((LoginStatus != null) && (LoginStatus.status == 1))
{
verasity = true;
}

Entity Framework - trying to insert null values when creating a new object

I'm having the same problem that a few of you have had - when trying to insert a new object, EF inserts null values for some of their properties, and the insert fails.
First let me describe the structure of our DB. Its an event management system, in which each event needs to be associated with a practice group, stored in a cache table but ultimately fetched from Active Directory. I manually created the join table - is that a problem? Anyway, so Event has a foreign key pointing to EventPracticeGroup, which has a foreign key pointing to PracticeGroupCache. PracticeGroupCache also has a RegionId pointing to the Regions table.
The problem comes when trying to insert a new EventPracticeGroup object. Below is the code I'm currently using:
var eventPracticeGroup = new EventPracticeGroup();
if (TryUpdateModel<EventPracticeGroup>(eventPracticeGroup))
{
/*
var eventId = EventScheduleRepository.GetById(Convert.ToInt32(Request.QueryString["EventScheduleId"])).EventId;
eventPracticeGroup.Event = EventRepository.GetById(eventId);
eventPracticeGroup.PracticeGroupCache = PracticeGroupCacheRepository.GetById(eventPracticeGroup.PracticeGroupCacheId);
eventPracticeGroup.PracticeGroupCache.Region = RegionRepository.GetById(eventPracticeGroup.PracticeGroupCache.RegionId);
EventPracticeGroupRepository.Add(eventPracticeGroup);
*/
var eventId = EventScheduleRepository.GetById(Convert.ToInt32(Request.QueryString["EventScheduleId"])).EventId;
var theEvent = new Event() { Id = eventId };
EventRepository.Repository.UnitOfWork.Context.AttachTo("Events",theEvent);
var practiceGroupCache = new PracticeGroupCache() { Id = eventPracticeGroup.PracticeGroupCacheId };
practiceGroupCache.Region = new Region() { Id = eventPracticeGroup.PracticeGroupCache.RegionId };
eventPracticeGroup.PracticeGroupCache = practiceGroupCache;
EventPracticeGroupRepository.Add(eventPracticeGroup);
EventPracticeGroupRepository.Save();
return RedirectToAction("Index");
}
Anyway... as you can see, I've just tried using stub objects (no help), and I've also tried actually fetching and setting the objects. The error I get is:
Cannot insert the value NULL into column 'Name', table 'XXXX.dbo.Regions'; column does not allow nulls. INSERT fails. The statement has been terminated.
Obviously name is not a key field. I have checked the EDMX XML - only the Id (primary key columns) have StoreGeneratedPattern set to Identity, as they should (they are int32 identity columns). Not a single foreign key has StoreGeneratedPattern set to identity.
if I set Regions.Name to allow nulls, PracticeGroupCaches.Description throws the same error. It seems that every linked object gets set to null. I did have a look with the debugger, when I used the now commented out code, nothing was null and everything had a value. I even got the RegionRepository to return all of the regions, just to see if one of them somewhere had a null name. None did. There are only 2 in my test DB. Our object context is shared per HTTP request.
Please can anyone help. At this point I would settle for using the dirtiest workaround as long as it worked.
Regards,
Jonathan.
Look what happens when you call this line:
EventPracticeGroupRepository.Add(eventPracticeGroup);
You are adding a new eventPracticeGroup to the context. But eventPracticeGroup has the other related objects:
eventPracticeGroup -> PracticeGroupCache -> Region
And you create new objects for those:
var practiceGroupCache = new PracticeGroupCache() {
Id = eventPracticeGroup.PracticeGroupCacheId };
practiceGroupCache.Region = new Region() {
Id = eventPracticeGroup.PracticeGroupCache.RegionId };
eventPracticeGroup.PracticeGroupCache = practiceGroupCache;
When you add the eventPracticeGroup to the context this whole object graph gets added which means that EF considers all three objects as new which have to be added to the DB. Since you only fill the Id properties other string properties (like Name or Description) are null. Because they are not nullable in the database the INSERT command fails.
But I guess that you don't want to insert the related entities into the DB anyway but only the eventPracticeGroup. So you need to attach them to the context before you add the new object, something like:
var practiceGroupCache = new PracticeGroupCache() {
Id = eventPracticeGroup.PracticeGroupCacheId };
EventRepository.Repository.UnitOfWork.Context.AttachTo(
"PracticeGroupCaches",practiceGroupCache);
practiceGroupCache.Region = new Region() {
Id = eventPracticeGroup.PracticeGroupCache.RegionId };
EventRepository.Repository.UnitOfWork.Context.AttachTo(
"Regions",practiceGroupCache.Region);
eventPracticeGroup.PracticeGroupCache = practiceGroupCache;
EventPracticeGroupRepository.Add(eventPracticeGroup);
BTW as a side note: About this EventRepository.Repository.UnitOfWork.Context.XXX take a look at Ladislav Mrnka's answer here: EF 4.0 IsAttachedTo extension method and error An object with the same key already exists
Try to Add: [DatabaseGenerated(DatabaseGeneratedOption.None)]
On your Id field, Like:
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
It seems like you already found the problem and the solution. In your DB schema it seems like the columns don't allow NULL values. So either change all these columns to allow NULL or don't insert null (this is what you currently are trying to do)

Entity Framework 4 - Get generated SQL for Updates/Inserts

With EF4, is it possible to get the generated SQL for Updates/Inserts rather than executing it... just like you can view the query SQL before it runs.
The reason is, I have a set of helper functions that execute SQL commands. For instance...
Decrement<Category>("ProductCount", categoryID);
SetNull<Product>("CategoryID", productID);
Which generates...
UPDATE Categories
SET ProductCount = ProductCount - 1
WHERE CategoryID = #CategoryID;
UPDATE Products
SET CategoryID = NULL
WHERE CategoryID = #ProductID;
I usually run several commands per operation, so with each helper function call, the SQL is generated and stored. When I call SaveChanges(), all of the commands are run at ONE time.
The only problem is that EF runs its commands separately behind the scenes, then I run the others right afterward. It would be ideal to run everything as one single command.
You can get it at design time with Sql Profiler, but I think you're meaning that you want it at run-time. Here's an example I found on how to do that:
public static void WriteGeneratedSql(EntityCommand cmd)
{
cmd.Prepare();
IServiceProvider isp = (IServiceProvider)EntityProviderFactory.Instance;
DbProviderServices mps = (DbProviderServices)isp.GetService(typeof(DbProviderServices));
EntityCommandDefinition definition = (EntityCommandDefinition)mps.CreateCommandDefinition(cmd);
int commandId = 1;
foreach (string commandText in definition.MappedCommands)
{
Console.WriteLine("Generated Command {0}:", commandId);
commandId++;
Console.WriteLine(commandText);
}
}
Found here.