db.Entry().CurrentValues.SetValues() remove my existing raw in Entity Framework C# - c#

When i try to update table with currentvalues.setvalues() it is removed my existing row in ef in c# .net
var oldItem = db.ClaimDetail.FirstOrDefault(w => w.id == objParam.id );
if (oldItem != null)
{
newItem.id = oldItem.id;
db.Entry(oldItem).CurrentValues.SetValues(newItem);
db.SaveChanges();
}
else
{
MyEntity.Add(newItem);
}
It should update raw not removed it.

Istead of this
db.Entry(oldItem).CurrentValues.SetValues(newItem);
use this
db.Entry(newItem).State = EntityState.Modified;

Related

Update A Table From A List

I have two tables. One of them is Customers which is in the database, the other one is ChangedCustomers, which comes from the user. I write the updated model, I guess there is something missing.
public async Task<int> UpdateCustomers (IENumerable<ChangedCustomers> changedCustomers
{
foreach(var item in changedCustomers)
{
_context.Customers.Updaate (new Customers()
{
CustomerName=item.CustomerName,
CustomerAddress=item.CustomerAddress
});
}
return await _context.SaveChangesAsync();
}
In Method I am not saying something like "update that row when Id values in Customers and ChangedCustomers tables are equal". I need this but I can't. How can I do it?
You are trying update the row by adding a new row? Thats what it looks like. If you want to update a specific row, you will need to fetch that row, then update the values.
foreach (var item in changedCustomers)
{
var customer = _context.Customers
.FirstOrDefault(x => x.CustomerId == item.CustomerId);
if (customer != null)
{
customer.CustomerName = item.CustomerName;
customer.CustomerAddress = item.CustomerAddress;
}
else
{
customer = new Customer
{
CustomerName = item.CustomerName,
CustomerAddress = item.CustomerAddress
}
_context.Add(customer);
}
}
_context.SaveChangesAsync();
FirstOrDefault() will retrieve the first value from _context.Customers that matches the expression, if it doesn't find any it will default to null. If it is not null then you can make the changes, if it is null, you could add a new value.
Here's general logic that you'd follow to update an existing record which could be placed within your loop:
//Find the entity already tracked based on table key
var entity = context.Customers.FirstOrDefault(item => item.YourTableID == id);
// Validate entity is not null
if (entity != null)
{
// Make changes to specific field
entity.Name = "Me";
// Update entity in it's entirety
entity = new Customers() { //Your logic here to build the updated entity }
// Save changes in database
context.SaveChanges();
}
Just mark the entities as modified and call SaveChanges.
Only UPDATE sql queries will be executed.
The code in #jaabh answer is very inefficient, since it pre-executes sql-queries SELECT, reading from the database those entities that we already have. This is unnecessary.
public async Task<int> UpdateCustomers(IEnumerable<ChangedCustomers> changedCustomers)
{
foreach (var item in changedCustomers)
{
_context.Entry(item).State = EntityState.Modified;
}
return await _context.SaveChangesAsync();
}

EntityFramework AutoDetectChangesEnabled disabled and facing issue

I have the following request in my repository :
public void UpdateAgreementAfterTerme(AgreementTerme agreement)
{
var agreementData = _agreementContext.Set<T_INSURANCE_AGREEMENT>()
.Include(f => f.T_COVERAGE_COMPONENT)
.SingleOrDefault(agg => agg.IDENTIFIER == agreement.Identifier && agg.IS_LAST_VERSION == true);
if (agreementData?.T_COVERAGE_COMPONENT == null || agreementData.T_COVERAGE_COMPONENT.Count == 0)
{
return;
}
foreach (var coverageComponent in agreementData.T_COVERAGE_COMPONENT.Where(coverage => coverage.HT_AMOUNT != null))
{
coverageComponent.HT_AMOUNT = coverageComponent.HT_AMOUNT * agreement.Coefficient;
}
_agreementContext.SaveChanges();
}
The T_INSURANCE_AGREEMENT has a list of T_COVERAGE_COMPONENT and I need to update the HT_AMOUNT with a coefficient of majoration.
When I disabled the AutoDetectChangesEnabled in my context, my unit tests shows me that the HT_AMOUNT is not updated. When the varaible is true, it is working fine.
What is wrong in my repository and how can I do it better ?
If you have a better way to loop over the T_COVERAGE_COMPONENT list than with a foreach, i'm interested
Fixed it by adding the _agreementContext.ChangeTracker.DetectChanges(); before the SaveChanges() in order to notify the _context that something has changed
Because you disabled AutoDetectChangesEnabled you have to explicitly inform the context which entity is being changed and how.
Update your foreach loop with this:
foreach (var coverageComponent in agreementData.T_COVERAGE_COMPONENT.Where(coverage => coverage.HT_AMOUNT != null))
{
coverageComponent.HT_AMOUNT = coverageComponent.HT_AMOUNT * agreement.Coefficient;
_agreementContext.Entry(coverageComponent).State = EntityState.Modified;
}

Update a Second Table or Model in EF - MVC

I need to update a second table in my EF, this table is my storage of purchase, i call this entity and search the value for change (Qty), i do the math comparasion and send back the data updated, but raise a error tell me "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"
How fix the error? Thanks
My Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(PurchaseDetails purchasedetails)
{
ViewBag.PurchaseID = new SelectList(db.Purchases, "PurchaseID", "Notes", purchasedetails.PurchaseID);
ViewBag.idArt = new SelectList(db.Art, "idArt", "des_art", purchasedetails.IdArt);
ViewBag.idAlmacen = new SelectList(db.Almacens, "idAlmacen", "des_alma", purchasedetails.IdAlmacen);
var cant_details = db.PurchaseDetails.Where(p => p.PurchaseDetailsID == purchasedetails.PurchaseDetailsID).FirstOrDefault();
var cantidad = purchasedetails.Qty - cant_details.Qty;
if (ModelState.IsValid)
{
db.Entry(purchasedetails).State = EntityState.Modified;
db.SaveChanges();
var stock_id = db.Stock.Where(s => s.idAlmacen == purchasedetails.IdAlmacen && s.idArt == purchasedetails.IdArt).FirstOrDefault();
stock_id.stcActual = stock_id.stcActual + cantidad;
db.Stock.Attach(stock_id);
var entry = db.Entry(stock_id);
entry.Property(e => e.stcActual).IsModified = true;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchasedetails);
}
I found The solution in this post
How do I detach objects in Entity Framework Code First?
i used AsNoTracking() in my line code
var cant_details = db.PurchaseDetails.Where(p => p.PurchaseDetailsID == purchasedetails.PurchaseDetailsID).FirstOrDefault();
and work perfectly
var cant_details = db.PurchaseDetails.AsNoTracking().Where(p => p.PurchaseDetailsID == purchasedetails.PurchaseDetailsID).FirstOrDefault();

Clean method in order to update collection in entity framework

i'm sorry if this question has already been asked, but i'm in trouble with my method of updating collection in Entity Framework.
Let me explain the situation :
- I have for example one model CUSTOMER with some properties and a collection of ORDERS (for example).
- Let's imagine we have an admin page on wich we can edit all the ORDERS for a customer, and when we submit the form, it will send us back the object CUSTOMERS with updated ORDERS (some added, some updated and some deleted).
For the moment i use something like this in order to compare old collection and new collection and determine which object i need to delete/update/add
var toRemove = new List<ORDERS>();
var toAdd = new List<ORDERS>();
foreach (
var order in
oldList.Where(
order =>
newList.FirstOrDefault(t => t.link_id == order.link_id) == null))
{
toRemove.Add(order);
}
foreach (
var order in
newList.Where(
order =>
oldList.FirstOrDefault(t => t.link_id == order.link_id) == null))
{
toAdd.Add(order);
}
foreach (var ORDERSe in toRemove)
{
bdd.ORDERS.Remove(ORDERSe);
}
foreach (var ORDERSe in toAdd)
{
ORDERSe.pjt_id = project_id;
bdd.ORDERS.Add(ORDERSe);
}
foreach (
var order in
newList.Where(
order =>
oldList.FirstOrDefault(t => t.link_id == order.link_id) != null))
{
var child = oldList.FirstOrDefault(t => t.link_id == order.link_id);
bdd.Entry(child).CurrentValues.SetValues(order);
}
But i'm unconfortable with this, because in my mind, entity framework should be able to do the work for me !
I was hoping something like :
customer.orders = newOrders;
Did i missed anything about entity framework or ?
Because when i do this, it just duplicate my orders.
Thanks in advance for your answer.
You can certainly make it cleaner using .Except() and .Intersect(), but the concept doesn't really change, AFAIK you still have to individually remove, update & add the entries in loops...
var oldList = new List<ORDERS>();
var newList= new List<ORDERS>();
var IdsToRemove = oldList.Select(t => t.link_id).Except(newList.Select(t => t.link_id));
var IdsToAdd = newList.Select(t => t.link_id).Except(oldList.Select(t => t.link_id));
var IdsToUpdate = newList.Select(t => t.link_id).Intersect(oldList.Select(t => t.link_id));
//remove
bdd.orders.where(x => IdsToRemove.Contains(x.link_id)).ForEach(x => bdd.Remove(x));
//add
foreach(var order in newList.Where(x -> IdsToAdd.Contains(x.link_id))
{
bdd.Orders.Attach(order);
bdd.Entries(order).EntityState = EntityState.Added;
}
//update
foreach(var order in newList.Where(x -> IdsToUpdate .Contains(x.link_id))
{
bdd.Orders.Attach(order);
bdd.Entries(order).EntityState = EntityState.Modified;
}
bdd.SaveChanges();
But i'm unconfortable with this, because in my mind, entity framework
should be able to do the work for me !
In fact, EF does the Work for you. Using the data context SaveChanges method EF should be able to save all your changes at once:
DbContext.SaveChanges()
For your convinience you can still override this method. Internally you should use something like this:
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
entry.Entity.ModifiedBy = UserName;
}
}
return base.SaveChanges();
}

Entity Framework 5.0 SaveChanges of Complex objects

I have a method that accepts an input parameter for a list of transactions with added or updated records.
I loop through the list to discover which is modified and whish is added using the:
context.Entry(item).State = System.Data.EntityState.Modified;
to set the state of each transaction.
The problem I have is because the transaction object has a relationship with TransactionType while I loop through the input parameter list of transactions, if there are more than one transaction with the same transaction id in the database I get the following error:
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
By the way I am using EF5 and Code First.
The method in concern is:
public TransactionList SaveTransactions(Transaction[] transactions)
{
try
{
using (EntityContext context = new EntityContext())
{
foreach (var item in transactions)
{
if (item.TransactionId > 0)
context.Entry(item).State = System.Data.EntityState.Modified;
else
context.Entry(item).State = System.Data.EntityState.Added;
}
context.SaveChanges();
}
return GetLatestTransactions();
}
## UPDATE ##
if I set the TransactionType for each item to null, I wont get any error and the rest of the transaction fields will be updated fine. i.e. TransAmount, Date, etc. The problem is that by setting the TransType to null I will never be able to change the type of my transaction.
using (EntityContext context = new EntityContext())
{
foreach (var item in transactions)
{
//set the fk to null
item.TransactionType = null;
if (item.TransactionId > 0)
{
context.Entry(item).State = System.Data.EntityState.Modified;
}
else
context.Entry(item).State = System.Data.EntityState.Added;
}
context.SaveChanges();
}
## UPDATE 2 ##
I just found another way that this would work but still is not the ideal for me. I get the single transaction per item and then set the values. I don't like this solution since .Single will do a roundtrip for each iteration.
foreach (var item in transactions)
{
var or = context.Transaction
.Include(t => t.Category)
.Include(t => t.TransactionReasonType)
.Include(t => t.TransactionType)
.Single(t => t.TransactionId == item.TransactionId);
if (item.TransactionId > 0)
{
context.Entry(or).CurrentValues.SetValues(item);
context.Entry(or).State = System.Data.EntityState.Modified;
}
The solution to my problem was to separate the add from the update. For update, fetch each row from database set the original values with the new ones. For add just add the new values to the context.
foreach (var item in transactions)
{
if (item.TransactionId > 0) //Update
{
var original = context.Transaction.Where(
t => t.TransactionId == item.TransactionId)
.FirstOrDefault();
original.TransactionType = context.TypeTransaction.Single(
p => p.TypeTransactionId == item.TransactionType.TypeTransactionId);
context.Entry(original).CurrentValues.SetValues(item);
}
else //Insert
{
item.TransactionType = context.TypeTransaction.Single(
p => p.TypeTransactionId == item.TransactionType.TypeTransactionId);
context.Transaction.Add(item);
}
}
context.SaveChanges();
Another way to workaround this issue:
foreach (var item in updated)
{
var original = db.MyEntities.Find(item.Id);
db.Entry(original).CurrentValues.SetValues(item);
}
db.SaveChanges();

Categories

Resources