I have an application which keeps updating every 1 second and I need to update my database accordingly.
I have this following getOrg function which grabs the data from API endpoint and insert into the database, but before inserting it checks hash for the duplicates, till here it works really well. But the problem is if something changes there will be new hash and as its a new hash it will go ahead and try to insert record but as OrgGuid is my primary key it is giving me:
SqlException: Violation of PRIMARY KEY constraint
So my question is how can I update only those columns which got changed?
public async Task getOrg()
{
var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
var response = await _client_NP.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
foreach (var item in model.resources)
{
string tmpmasterhash = HashClass.CreateMD5(item.guid + item.name + item.created_at + item.updated_at);
if (!_DBcontext.Organizations.Any(o => o.Orghash == tmpmasterhash))
{
var Orgs = new Organizations
{
OrgGuid = Guid.Parse(item.guid),
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Orghash = tmpmasterhash,
Timestamp = DateTime.Now,
Foundation = 3
};
_DBcontext.Organizations.Add(Orgs);
}
}
await _DBcontext.SaveChangesAsync();
}
Retrieve the organization for the given Guid first, and if it exists, update it. If it doesn't exist, add it
var g = Guid.Parse(item.guid);
var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if(x == null)
_DBcontext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Orghash = tmpmasterhash,
Timestamp = DateTime.Now,
Foundation = 3
});
else{
x.Name = item.name;
.... //whatever else you want to update
}
await _DBcontext.SaveChangesAsync();
PS; if the hash changes it's not a good idea to dedupe based on it; by definition you have an ID that doesn't change, don't bother with your hash - use the Guid you use for the PK?
Per my comment, hash is really only useful to know whether you should run a DB update, but honestly, it's probably useless. I would expect that EF won't bother to save the object if you update the properties to the same values they are already so calcing a hash is a waste of time but here's how you might:
var g = Guid.Parse(item.guid);
var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if(x == null){
_DBcontext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Orghash = tmpmasterhash,
Timestamp = DateTime.Now,
Foundation = 3
});
await _DBcontext.SaveChangesAsync();
} else if(tmpmasterhash != x.Orghash) {
x.Name = item.name;
.... //whatever else you want to update
await _DBcontext.SaveChangesAsync();
}
Attach
Attaches a record to the context. This is optimistic behavior: Entity Framework expects the record to exist and only observes other records that make use of this attached record. Any modifications made to the attached record are not passed on to the database.
Read this: https://learn.microsoft.com/en-us/ef/ef6/saving/change-tracking/entity-state
Original answer: https://pt.stackoverflow.com/a/38195/14630
Related
I have to update one field in the row of the table after fetching two records from the same row. As an easiest practice I have fetched two records individually, created a new value and then updating that particular property through Entity framework. I think there is a better way to do the same thing with less code. If any body can suggest please.
if (objModel.amountpaid==0)
{
using (estatebranchEntities db=new estatebranchEntities())
{
int rentVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.rent).SingleOrDefault());
int balanceVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.balance).SingleOrDefault());
int balanceUpdateVar = (rentVar + balanceVar);
var propInfo = new PropertyDetail() { balance = balanceUpdateVar };
//var result = (from a in db.PropertyDetails
// where a.propertyid == objVM.propertyid
// select new PropertyDetail
// {
// rent = a.rent,
// balance = a.balance
// }).ToList();
db.PropertyDetails.Attach(propInfo);
db.Entry(propInfo).Property(z => z.balance).IsModified = true;
db.SaveChanges();
}
}
Here is what I think you can do.
Fetch the data once and update once.
using (estatebranchEntities db=new estatebranchEntities())
{
var propDetails = db.PropertyDetails.FirstOrDefault(m => m.propertyid == objVM.propertyid);
if (propDetails != null)
{
int rentVar = Convert.ToInt32(propDetails.rent);
int balanceVar = Convert.ToInt32(propDetails.balance);
int balanceUpdateVar = rentVar + balanceVar;
//now do the update
propDetails.balance = balanceUpdateVar;
db.Entry(proDetails).State = EntityState.Modified;
db.SaveChanges();
}
}
if you need to use the rentVar,balanceVar or the balanceUpdateVar, outside of the using statement then declare them outside it.
I am using EF6 with C# in Asp.net 4.5. I want to update existing entry with new entry. For example:
Customer customer = new Customer()
{
Address = txtAddress.Text,
Name = txtName.Text,
UserName = txtUserName.Text
};
if(Id==0)
{
//INSERT
customer.Password = MyHelper.md5(txtPassword.Text);
customer.CreatedDate = DateTime.Now;
db.Customers.Add(customer);
}
else
{
//UPDATE
Customer currentCustomer = db.Customers.FirstOrDefault(x => x.Id == Id);
customer.Id = Id;
if (!String.IsNullOrEmpty(txtPassword.Text))
customer.Password = MyHelper.md5(txtPassword.Text);
db.Entry(currentCustomer).CurrentValues.SetValues(customer);
}
It inserts new entry and updates Address, Name, UserName properties successfully. But in update event, it changes CreatedDate and Password fields to NULL. Because I didn't specified Password and CreatedDate fields again. I want EF to skip these fields in update. But as I understand, it assumes these fields as null.
Is it a bug, or a feature of Entity Framework. So, what should I do if I want to update an entry with another entry? Do I have to specify all values again?
This is an issue with your process. customer is an in-memory representation of a new Customer. You define .Password and .CreatedDate only when Id==0 therefore it isn't available during your update routine. And then your .SetValues(customer) declaration sets the existing currentCustomer.Password and currentCustomer.CreatedDate to null
Update your code to only update properties from customer that you wish to be updated in currentCustomer. Like so:
//UPDATE
Customer currentCustomer = db.Customers.FirstOrDefault(x => x.Id == Id);
currentCustomer.Address = customer.Address;
db.Customers.Update(currentCustomer);
For an even cleaner approach, you could revise entirely to:
Customer c;
if(Id == 0) {
c = new Customer(){
Password = MyHelper.md5(txtPassword.Text),
CreatedDate = DateTime.Now
};
}
else
c = db.Customers.FirstOrDefault(x => x.Id == Id);
if (c != null) {
c = c{
Address = txtAddress.Text,
Name = txtName.Text,
UserName = txtUserName.Text
};
if(Id != 0)
db.Customers.Update(c);
else
db.Customers.Insert(c);
db.SaveChanges();
}
I am new to entity framework and LINQ. I am stuck at an issue where I need to firstly check if the record already exists, if it exists then I need to update the record with column RESUMEID accordingly. If not then I need to add the record. I am able to add successfully but I don't know how to update the record in LINQ.
Below is my attempt:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ReferralViewModel viewModel)
{
var candidateId = User.Identity.GetUserId();
// I AM CONFUSED ABOUT BELOW STATEMENT
var IsDupeReferral = _context.Referrals
.Where(r => (r.CandidateId == candidateId)
&& (r.CompanyId == viewModel.CompanyId) && (r.SkillId == viewModel.SkillId))
.Select(r=>r.ReferralId).SingleOrDefault();
if(IsDupeReferral!=0)
{
//IF I FIND DUPE REFERRAL RECORD I WANT TO UPDATE SOME OF THE VALUES IN THAT
_context.Referrals.Where(r => r.ReferralId == IsDupeReferral).
AND UPDATE r.resumeId with viewModel.ResumeId // How to do this?
// NOT SURE ABOUT BELOW LINE EITHER
_context.SaveChanges();
}
else
{
// BELOW CODE IS WORKING FINE
var referral = new Referral
{
ReferralName = viewModel.ReferralName,
ResumeId = viewModel.ResumeId,
CandidateId = candidateId,
DegreeId = viewModel.DegreeId,
CoverLetterId = viewModel.CoverLetterId,
SkillId = viewModel.SkillId
};
if (!string.IsNullOrEmpty(viewModel.TempCompany))
{
var newCompany = new Company
{
CompanyName = viewModel.TempCompany
};
newCompany.Referrals.Add(referral);
_context.Companies.Add(newCompany); ;
}
else
{
referral.CompanyId = viewModel.CompanyId.Value;
_context.Referrals.Add(referral);
}
_context.SaveChanges();
}
return RedirectToAction("ReferralCenter");
}
Here's the solution
//IF I FIND DUPE REFERRAL RECORD I WANT TO UPDATE SOME OF THE VALUES IN THAT
var referral = _context.Referrals.FirstOrDefault(r => r.ReferralId == IsDupeReferral);
// AND UPDATE r.resumeId with viewModel.ResumeId
if (referral !=null) {
referral.resumeId = viewModel.ResumeId;
_context.Entry(referral).State = System.Data.EntityState.Modified;
_context.SaveChanges();
}
Actually, you don't need getting the IsDupeReferral and then request the record again. Try to combine your code as the following:
var referral = _context.Referrals
.Where(r => (r.CandidateId == candidateId)
&& (r.CompanyId == viewModel.CompanyId) && (r.SkillId == viewModel.SkillId)).SingleOrDefault();
if (referral !=null) {
referral.resumeId = viewModel.ResumeId;
_context.Entry(referral).State = System.Data.EntityState.Modified;
_context.SaveChanges();
}
else {
// add a new record
}
Referral referral = _context.Referrals.FirstOrDefault(r=> r.ReferralId = SomeId);
if(referral == null) // then referral does not exist - add it
{
referral = new Referral{
ReferralName = viewModel.ReferralName,
ResumeId = viewModel.ResumeId,
CandidateId = candidateId,
DegreeId = viewModel.DegreeId,
CoverLetterId = viewModel.CoverLetterId,
SkillId = viewModel.SkillId
};
_context.Referrals.Add(referral);
}
else // referral already exists - update its values
{
//make changes to referral
referral.ReferralName = viewModel.ReferralName;
referral.ResumeId = viewModel.ResumeId;
referral.CandidateId = candidateId;
referral.DegreeId = viewModel.DegreeId;
referral.CoverLetterId = viewModel.CoverLetterId;
referral.SkillId = viewModel.SkillId;
}
_context.SaveChanges(); //no matter added or updated - save the changes
I'm somewhat new to EF 6.0 so I'm pretty sure I'm doing something wrong here.
there are two questions related to the problem
what am I doing wrong here
what's the best practice to achieve this
I'm using a code first model, and used the edmx designer to design the model and relationships, the system needs to pull information periodically from a webservice and save it to a local database (SQL Lite) in a desktop application
so I get an order list from the API, when I populate and try to save Ticket, I get a duplicate key exception when trying to insert TicketSeatType -
how do I insert the ticket to dbContext, so that It doesn't try and re-insert insert TicketSeatType and TicketPriceType, I have tried setting the child object states to unchanged but it seems to be inserting
secondly, what would be the best practice to achieve this using EF ? it just looks very inefficient loading each object into memory and comparing if it exists or not
since I need to update the listing periodically, I have to check against each object in the database if it exists, then update, else insert
code:
//read session from db
if (logger.IsDebugEnabled) logger.Debug("reading session from db");
dbSession = dbContext.SessionSet.Where(x => x.Id == sessionId).FirstOrDefault();
//populate orders
List<Order> orders = (from e in ordersList
select new Order {
Id = e.OrderId,
CallCentreNotes = e.CallCentreNotes,
DoorEntryCount = e.DoorEntryCount,
DoorEntryTime = e.DoorEntryTime,
OrderDate = e.OrderDate,
SpecialInstructions = e.SpecialInstructions,
TotalValue = e.TotalValue,
//populate parent refernece
Session = dbSession
}).ToList();
//check and save order
foreach (var o in orders) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.Id).FirstOrDefault();
if (dbOrder != null) {
dbContext.Entry(dbOrder).CurrentValues.SetValues(o);
dbContext.Entry(dbOrder).State = EntityState.Modified;
}
else {
dbContext.OrderSet.Add(o);
dbContext.Entry(o.Session).State = EntityState.Unchanged;
}
}
dbContext.SaveChanges();
//check and add ticket seat type
foreach (var o in ordersList) {
foreach (var t in o.Tickets) {
var ticketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
};
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == ticketSeatType.Id).FirstOrDefault();
if (dbTicketSeatType != null) {
dbContext.Entry(dbTicketSeatType).CurrentValues.SetValues(ticketSeatType);
dbContext.Entry(dbTicketSeatType).State = EntityState.Modified;
}
else {
if (!dbContext.ChangeTracker.Entries<TicketSeatType>().Any(x => x.Entity.Id == ticketSeatType.Id)) {
dbContext.TicketSeatTypeSet.Add(ticketSeatType);
}
}
}
}
dbContext.SaveChanges();
//check and add ticket price type
foreach (var o in ordersList) {
foreach (var t in o.Tickets) {
var ticketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
SeatCount = t.TicketPriceType.SeatCount,
Description = t.TicketPriceType.Description
};
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == ticketPriceType.Id).FirstOrDefault();
if (dbTicketPriceType != null) {
dbContext.Entry(dbTicketPriceType).CurrentValues.SetValues(ticketPriceType);
dbContext.Entry(dbTicketPriceType).State = EntityState.Modified;
}
else {
if (!dbContext.ChangeTracker.Entries<TicketPriceType>().Any(x => x.Entity.Id == ticketPriceType.Id)) {
dbContext.TicketPriceTypeSet.Add(ticketPriceType);
}
}
}
}
dbContext.SaveChanges();
//check and add tickets
foreach (var o in ordersList) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
foreach (var t in o.Tickets) {
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
Description = t.TicketPriceType.Description,
SeatCount = t.TicketPriceType.SeatCount,
},
TicketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
},
Order = dbOrder
};
//check from db
dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();
if (dbTicket != null) {
dbContext.Entry(dbTicket).CurrentValues.SetValues(t);
dbContext.Entry(dbTicket).State = EntityState.Modified;
dbContext.Entry(dbTicket.Order).State = EntityState.Unchanged;
dbContext.Entry(dbTicketSeatType).State = EntityState.Unchanged;
dbContext.Entry(dbTicketPriceType).State = EntityState.Unchanged;
}
else {
dbContext.TicketSet.Add(ticket);
dbContext.Entry(ticket.Order).State = EntityState.Unchanged;
dbContext.Entry(ticket.TicketSeatType).State = EntityState.Unchanged;
dbContext.Entry(ticket.TicketPriceType).State = EntityState.Unchanged;
}
}
}
dbContext.SaveChanges();
UPDATE:
Found the answer, it has to do with how EF tracks references to objects, in the above code, I was creating new entity types from the list for TicketPriceType and TicketSeatType:
foreach (var o in ordersList) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
foreach (var t in o.Tickets) {
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
Description = t.TicketPriceType.Description,
SeatCount = t.TicketPriceType.SeatCount,
},
TicketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
},
Order = dbOrder
};
....
in this case the EF wouldn't know which objects they were and try to insert them.
the solution is to read the entities from database and allocate those, so it's referencing the same entities and doesn't add new ones
foreach (var t in o.Tickets) {
//check from db
dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = dbTicketPriceType,
TicketSeatType = dbTicketSeatType,
Order = dbOrder
};
...}
Don't you think that you are trying to write very similar codes for defining the state of each entity?
We can handle all of these operations with a single command.
You can easily achieve this with the newly released EntityGraphOperations for Entity Framework Code First. I am the author of this product. And I have published it in the github, code-project (includes a step-by-step demonstration and a sample project is ready for downloading) and nuget. With the help of InsertOrUpdateGraph method, it will automatically set your entities as Added or Modified. And with the help of DeleteMissingEntities method, you can delete those entities which exists in the database, but not in the current collection.
// This will set the state of the main entity and all of it's navigational
// properties as `Added` or `Modified`.
context.InsertOrUpdateGraph(ticket);
By the way, I feel the need to mention that this wouldn't be the most efficient way of course. The general idea is to get the desired entity from the database and define the state of the entity. It would be as efficient as possible.
I have a problem saving my data in Entity Framework.
I have done a function that overrides the saveChanges of my dbContext, because I use it to set some audit data and do other stuff. In one case I have a big trouble.
I have to insert, for each new record, some records in another table, referring to the new with a foreign key. If the new record is only one, it is all ok. But if the record are 2 or more, I get errors.
I have a for, looping on all the new records, in this way:
var addedAuditedEntities = ChangeTracker.Entries()
.Where(p => p.State == EntityState.Added)
.Select(p => p.Entity);
foreach (var added in addedAuditedEntities)
{
.....
}
I write my attempts.
1) in this way, it gives error at the second new record, saying that there is already an unique data in the tab TabAssVeicoliTerminali (it is a table with an unique index on the couple IDTER-IDTEV), I think because it inserts temporary all records with IDTEV = 0
else if (added.GetType() == typeof(TabTessereVeicoli))
{
TabTessereVeicoli i = (TabTessereVeicoli)added;
i.DATA_INS = now;
i.USER_INS = mdlImpostazioni.p.UserName;
var listaterV = new List<TabAssVeicoliTerminali>();
foreach (var ter in MainWindow.dbContext.TabTerminali)
{
var tt = new TabAssVeicoliTerminali();
tt.MODIFICATO = true;
tt.ABILITATO = true;
tt.IDTER = ter.ID;
tt.IDTEV = i.ID;
tt.DATA_INS = now;
tt.USER_INS = mdlImpostazioni.p.UserName;
listaterV.Add(tt);
}
MainWindow.dbContext.BulkInsert(listaterV);
}
2) So, I tried to save before, to try to set the ID of the new record (it is autoincremental id) and use something != 0 in the IDTEV foreign key. But, it saves all, also the other new records. So, it saves correctly the fk in other table, but only for the first new record:
else if (added.GetType() == typeof(TabTessereVeicoli))
{
TabTessereVeicoli i = (TabTessereVeicoli)added;
i.DATA_INS = now;
i.USER_INS = mdlImpostazioni.p.UserName;
var listaterV = new List<TabAssVeicoliTerminali>();
base.SaveChanges();
foreach (var ter in MainWindow.dbContext.TabTerminali)
{
var tt = new TabAssVeicoliTerminali();
tt.MODIFICATO = true;
tt.ABILITATO = true;
tt.IDTER = ter.ID;
tt.IDTEV = i.ID;
tt.DATA_INS = now;
tt.USER_INS = mdlImpostazioni.p.UserName;
listaterV.Add(tt);
}
MainWindow.dbContext.BulkInsert(listaterV);
}
3) I have also tried without the BulkInsert, but I get the same results.
How can I reach my goal?
Starting from the hint of Ben Robinson in the comments of this question, I have found a solution.
It works, but it stil is the "perfect solution", because to do it, I had to comment the part of the bulk insert, so it is a bit slow with a lot of new records.
Anyway, the code I have posted in the question, is now:
else if (added.GetType() == typeof(TabTessereVeicoli))
{
TabTessereVeicoli i = (TabTessereVeicoli)added;
i.DATA_INS = now;
i.USER_INS = mdlImpostazioni.p.UserName;
var listaterV = new List<TabAssVeicoliTerminali>();
foreach (var ter in MainWindow.dbContext.TabTerminali)
{
var tt = new TabAssVeicoliTerminali();
tt.MODIFICATO = true;
tt.ABILITATO = true;
tt.IDTER = ter.ID;
tt.IDTEV = i.ID;
tt.DATA_INS = now;
tt.USER_INS = mdlImpostazioni.p.UserName;
//listaterV.Add(tt);
i.TabAssVeicoliTerminali.Add(tt);
}
//MainWindow.dbContext.BulkInsert(listaterV);
}
Let me know if you know how can I reach my goal using also the bulkInsert!