I have a table Estimation which has an column EstimationNo,i am trying to get the max EstimationNo like this-
var result = cont.SalesEstimateCont.Where(x => x.Org_ID == CurrentOrgId);
var estimationMaxNo = result.Any() ? result.Max(x => x.EstimateNo) + 1 : 1;
var DigitalEstimate = new SalesEstimate()
{
EstimateNo=estimationMaxNo;
};
cont.Estimate.Add(DigitalEstimate );
cont.Savechanges();
but the problem is, if same table is saving by different users at same time its saving the same EstimationNo for both users. like- 10,10
Now, how to handle this issue..please give some solution.
Best strategy is to let db engine (I assume that it is SQL Server) handle incrementing of EstimateNo field. This can be done with identity specification which can be added to normal not primary key field also.
ALTER TABLE SalesEstimateCont drop column EstimateNo
go
ALTER TABLE SalesEstimateContadd Add EstimateNo int NOT NULL IDENTITY (1,1)
Please note: if you have existing data or some data should be modified, you may need some extra effort to achieve this (i.e with temp tables and by setting IDENTITY INSERT ON)
I got a simple answer.I just had to use transacationScope class.
and lock the resource table. like this-
using (TransactionScope scope = new TransactionScope())
{
cont.Database.ExecuteSqlCommand("SELECT TOP 1 * FROM Sales__Estimate WITH (TABLOCKX, HOLDLOCK)");
var result = cont.SalesEstimateCont.Where(x => x.Org_ID == CurrentOrgId);
var estimationMaxNo = result.Any() ? result.Max(x => x.EstimateNo) + 1 : 1;
var DigitalEstimate = new SalesEstimate()
{
EstimateNo=estimationMaxNo;
};
cont.Estimate.Add(DigitalEstimate );
cont.Savechanges();
}
If you can make EstimateNo an Identity column, that is the easiest/best way to fix this. If you can change this to a Guid, that would be another easy way to fix this as PK would be unique regardless of the user.
If you can't do either of these and you must take Max() manually, you might want to consider creating another table that stores the next available number there. Then you can create a new SqlCommnand with a Serializable transaction to lock the table, update the # by 1 and select it back. If two update commands hit at the same time, only one update will run and won't let go until that connection with Serializable transaction gets closed. This allows you to select the newly updated number before the other update runs and get the now "unique" next number.
You can OrderByDescending and then Take the the first record
var estimationMaxNo = result.OrderByDescending(x => x.EstimateNo).Take(1);
It can be done in a single command. You need to set the IDENTITY property for primary id
ALTER TABLE SalesEstimateCont ADD Org_ID int NOT NULL IDENTITY (1,1) PRIMARY KEY
Related
I have a database table:
Correlation
------------
JobId (PK),
SomeId (PK),
Order (PK),
MapGuid
I am trying to add a new Correlation (1, 1, 1) into existing Correlation table. The order of existing entities will be updated after Add. The existing entity at (1, 1, 1) will be updated (1,1,2).
However, on add, I get an error:
The instance of entity type 'Correlation' cannot be tracked because another instance with the same key value for {'JobId', 'SomeId', 'Order'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
relevant piece of Code:
if (actionType == ActionType.Create)
{
_unitOfWork.Repository<Correlation>().Insert(
new Correlation
{
JobId = jobId,
SomeId = someId,
Order = order,
MapGuid = newGuid,
});
}
if (actionType == ActionType.Update)
{
// update the title, description, imagefile.
var coors = _unitOfWork.Repository<Correlation>()
.Get()
.Where(a => a.JobId == jobId && a.SomeId == someId && a.Order = order)
.FirstOrDefault();
// set updated values
if (coors != null)
{
coors.Order = newOrder;
}
}
}
How can I add a new entity and then update existing entities at the same time.
You've pretty much answered your own question.
"The order of existing entities will be updated after Add. The existing entity at (1, 1, 1) will be updated (1,1,2)."
EF cannot insert a 1,1,1 when one already exists, and in your case the context knows about it. Even if the context didn't know about it (detached) you'd still get potential errors about inserting duplicate rows.
The main issue you have here is in your design. You potentially have an existing record (1,1,1) that you want to insert a new (1,1,1) and make that old record (1,1,2). Of course that needs to cascade because any existing (1,1,2) would become (1,1,3) and so-forth. The design issue is that you've made the PK of your table a composite key, and a meaningful key at that, as opposed as a dumb, simple, meaningless key. Keys are an identifier to a unique record. By design they should be immutable. (Cannot change)
To solve your issue the easiest way, change your table design to:
Correlation
------------
CorrelationId (PK) Identity 1,1
JobId (FK),
SomeId (FK),
Order,
MapGuid
And create an index on JobId + SomeId + Order since that will be significant for whatever this table will probably be used for. You can even enforce no duplicates, but I'd be wary of that because there may be no guarantee that EF will update the existing rows before inserting the new one.
Now, when you go to do an insert:
var existingCorrelations = context.Correlations.Where(x => x.JobId == jobId && x.SomeId == someId).ToList();
foreach(var correlation in existingCorrelations)
{
correlation.Order += 1;
}
context.Correlations.Add( new Correlation { JobId = jobId, SomeId = someId, Order = 1, MapGuid = mapGuid } );
context.SaveChanges();
In this case the Correlation has a PK called correlation ID. It is a meaningless key in the sense that none of your business logic will ever depend on it except as an identifier for this specific record. Now you can change the Order of an existing record.
Agreeing with Panagoitis about the Generic repository. I don't see it adding anything of value in your case, it helps to keep things simple. The above example should be easy enough to revert back to the repository pattern if that is what you're stuck with.
If you cannot change the data schema, then you're pretty much painted into an ugly little corner of the bathroom. It can still be worked around, but that's a lot more fiddley work loading existing records and moving the non-key data along the chain all the way to a new row on the end, and replacing the first row with the new data. (don't go there, really. :)
After review the answers and comments:
My DB structure is
1) TABLE BASE_ID ( ID int PK (IDENTITY(1,1))
2) TABLE USERS (ID int PK, FK(BASE_ID.ID).. rest of fields)
3) TABLE THREADS (ID int PK,(IDENTITY(1,1), UserID FK (BASE_ID.ID) )
4) TABLE_POSTS (ID int PK,(IDENTITY(1,1), UserID FK(BASE_ID.ID), ThreadID FK(THREAD.ID) )
I need to insert new values in these tables in one transaction using EF(6.0)
I stuck that EF create two records in BASE_ID even if I do:
//Create new base entry
BASE_ID base = new BASE_ID();
//Create new User
USERS user = new USERS();
//Init fields
THREADS thread = new THREADS();
POSTS post = new POSTS();
post.Thread =thread;// linked property not (int ThreadID since don't yet know it
base.User = user; //linker property
base.Posts.Add(post); //also linked property
//base.Threads.Add(thread);//this was removed. But problem not solved.
oEntity.BASE_ID.Add(base);
oEntity.SaveChanges(); // And here i have already two new BASE_ID's
UPDATE:
DB after commit becomes:
Posts.UserID=n //Lets say 1
Threads.UserId=n,//same as above
Users.ID = n+1; // incremented so 2
I was trying to add in various combination, like
user.BASE_ID = base;
oEntity.USERS.Add(user);
oEntity.THREADS.Add(thread);
oEntity.POSTS.Add(post);
oEntity.SaveChanges(); // same result as above.
I am really stuck.
Question: How to force (or explain to) EF that I need only ONE BASE_ID used for all inserted entries in post thread and user.
Thanks in advance.
This line is making it double
base.Threads.Add(thread);
You already added it before with:
post.Thread =thread;
So it would just be:
//Create new base entry
BASE_ID base = new BASE_ID();
POSTS post = new POSTS() {Thread = new THREADS()};
base.Posts.Add(post);
base.User = new USERS();
oEntity.BASE_ID.Add(base);
oEntity.SaveChanges();
Entity Framework automatically keeps track of the related data, so when you add a new entity it's reference properties are tracked by EF (it's being marked as Added by the ObjectStateManager).
When you add a related entity, it's automatically being marked as added, so if you re-add it again it's added twice, so per your code, instead of adding each entity separately (I take it oEntity is your context), just add the central object, then set its reference properties (i.e. base.User etc.).
Thanks all for your help.
Actually even initial code was fine.
The cause was AfterInsert trigger on USERS table which was adding entry to BASE_ID.
So I mark your comments as Answer and close the thread.
Thanks again.
i'm have collection in my client C# app which consist of the 5 columns. In the DB I have created a User defined table type (UDTT) of six columns. the first column is an autom increment which I will be using to fetch each row stored in it. table structure is pSelCRSInfo
while(#cntr <= #pProgDuration)
begin
select
#CRSID=CRSID,
#YearNo=YearNo,
#IsCompulsory=IsCompulsory,
#EntDT=EntDT,
#EmpID=EmpID
from #pSelCRSInfo where CntrNo=#cntr
insert into DefaultCourses
(PRGID,CRSID,YearNo,IsCompulsory,EntDT,EmpID)
values (#newPRGID,#CRSID,#YearNo,#IsCompulsory,#EntDT,#EmpID)
set #cntr = #cntr + 1
end
in it's definition I have added
create type dbo.SelectedCourses
as table
(
CntrNo int not null IDENTITY(1, 1),
CRSID int,
YearNo int,
IsCompulsory varchar(20),
EntDT datetime,
EmpID int
);
go
At the moment i'm struggling with how to pass 5 column collection to a collection that requires 6 column?
Typically do people create UDTT structures with auto increment columns to accept collections? because I could generate the numbers generated by the auto increment column from the client then send that with the collection
thanks
I would say it depends entirely on you. If auto-incremented values suit your purpose then definitely use them. I'd do it.
so assume I have created a UDTT with a auto increment column, then still I should pass 6 column collection like the one below and leave the first column empty
//leave the first column empty
acd.dtCourses.Rows[CollCounter][0] = string.Empty
acd.dtCourses.Rows[CollCounter][1] = dt.Rows[i][2];
acd.dtCourses.Rows[CollCounter][2] = dt.Rows[i][3];
acd.dtCourses.Rows[CollCounter][3] = dt.Rows[i][4];
acd.dtCourses.Rows[CollCounter][4] = DateTime.Today;
acd.dtCourses.Rows[CollCounter][5] = myAcedemics.EmpID;
dtCourses is a datatable to accept the collection and my acd is an object of the business class Academics
RejectChanges doesn't rollback the autonumber (I understand why thanks to this post). But, when I save a row. It overrides the autonumber which was originally returned to me. I need to know the ID of the parent row so I can create the related records.
Perhaps there is a better way I should be creating the related rows?
I saw this post which recommended changing the AutoIncrement to -1 and AutoSeed to -1. But, that does't make sense nor did it work for me.
In my code there are two tables, Shipments and Shipment_Package_Details. It's a 1-many relationship.
r = this.allertDataSet3.Shipments.NewShipmentsRow();
log.Debug("The new ShipmentRow ID is " + r.ShipmentID); // ID == 1
this.allertDataSet3.Shipments.RejectChanges();
r = this.allertDataSet3.Shipments.NewShipmentsRow();
log.Debug("The new ShipmentRow ID is " + r.ShipmentID); // ID == 2
r.NumPkgs = p.Length;
this.allertDataSet3.Shipments.Rows.Add(r);
// Create Related Row
MyCompany.MyNamespace.allertDataSet3.Shipment_Package_DetailsRow pd = this.allertDataSet3.Shipment_Package_Details.NewShipment_Package_DetailsRow();
pd.ShipmentID = r.ShipmentID; // ID == 2 here.
pd.TrackingNumber = trackingNumbers[i];
this.allertDataSet3.Shipment_Package_Details.Rows.Add(pd);
this.shipmentsTableAdapter.Update(this.allertDataSet3);
// At this point if i set a breakpoint and look at database, it's been committed to the database with ID == 1.
// The following line will fail since I set the package details row to ID == 2.
this.shipment_Package_DetailsTableAdapter.Update(this.allertDataSet3);
Okay, so I'm new to both EF and LINQ. I have figured out how to INSERT and DELETE but for some reason UPDATE seems to escape my grasp.
Here is a sample of my code:
EntityDB dataBase = new EntityDB();
Customer c = new Customer
{
Name = "Test",
Gender = "Male
};
dataBase.Customers.AddObject(c);
dataBase.SaveChanges();
The above creates and adds a record just fine.
Customer c = (from x in dataBase.Customers
where x.Name == "Test"
selext x).First();
dataBase.Customers.DeleteObject(c);
dataBase.SaveChanges();
The above effectively deletes the specified record.
Now how do I update? I can't seem to find an "UpdateObject()" method on the entity collection.
Just modify one of the returned entities:
Customer c = (from x in dataBase.Customers
where x.Name == "Test"
select x).First();
c.Name = "New Name";
dataBase.SaveChanges();
Note, you can only update an entity (something that extends EntityObject, not something that you have projected using something like select new CustomObject{Name = x.Name}
//for update
(from x in dataBase.Customers
where x.Name == "Test"
select x).ToList().ForEach(xx => xx.Name="New Name");
//for delete
dataBase.Customers.RemoveAll(x=>x.Name=="Name");
They both track your changes to the collection, just call the SaveChanges() method that should update the DB.
In most cases #tster's answer will suffice. However, I had a scenario where I wanted to update a row without first retrieving it.
My situation is this: I've got a table where I want to "lock" a row so that only a single user at a time will be able to edit it in my app. I'm achieving this by saying
update items set status = 'in use', lastuser = #lastuser, lastupdate = #updatetime where ID = #rowtolock and #status = 'free'
The reason being, if I were to simply retrieve the row by ID, change the properties and then save, I could end up with two people accessing the same row simultaneously. This way, I simply send and update claiming this row as mine, then I try to retrieve the row which has the same properties I just updated with. If that row exists, great. If, for some reason it doesn't (someone else's "lock" command got there first), I simply return FALSE from my method.
I do this by using context.Database.ExecuteSqlCommand which accepts a string command and an array of parameters.
Just wanted to add this answer to point out that there will be scenarios in which retrieving a row, updating it, and saving it back to the DB won't suffice and that there are ways of running a straight update statement when necessary.