Entity Framework Update Problem - c#

I am using Entity Framework & LINQ to retrieve data. I am having a problem with the following:
var customer= db.customers.where(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
The fields are changing in the database before I call:
db.SaveChanges();
How do I avoid this?

As others have said, I believe that you are using your context in another place as well and that other location is calling savechanges and updating everything. Try doing what #Evan suggested with a using statment to make sure you have a fresh context.
AsNoTracking will not ensure that you get a entity that is not cached in the database, its purpose is to not put the objects inside the context. If you use AsNoTracking and then change the entities returned in the query you will need to attach them as modified to the context before calling savechanges or else they won't be updated.
var customer= db.customers.AsNoTracking().Single(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
ctx.customers.Attach(customer);
ctx.ObjectStateManager.ChangeObjectState(customer, System.Data.EntityState.Modified);
I would have just commented on the other posts but don't have enough rep yet.

Check whether you are passing the db object to some other method, and SaveChanges() is called there?
Or check whether you have a catch block of an exception and you might be using SaveChanges() in the catch block to log error message?
(These are common programming mistakes)

The fields are changing in the database before I call
If you mean changing as in changing outside of application, changes in SQL Management Studio for example. Entity Framework cannot detect those changes, so as a result you might get stale objects that was cached by Entity Framework. To prevent receiving cached object and get the up-to-date values from database, use AsNoTracking.
Try putting AsNoTracking():
var customer= db.customers.AsNoTracking().where(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
db.SaveChanges();
Or if your problem is to detect concurrent updates(unfortunate terminology, it doesn't apply to UPDATE only) to same row, use rowversion(aka timestamp) field type; then on your .NET code add Timestamp attribute on the property. Example: http://www.ienablemuch.com/2011/07/entity-framework-concurrency-checking.html
public class Song
{
[Key]
public int SongId { get; set; }
public string SongName { get; set; }
public string AlbumName { get; set; }
[Timestamp]
public virtual byte[] Version { get; set; }
}
UPDATE (after your comment):
If you really has no intent to persist your object changes to database. Try detaching the object.
Try this:
var customer= db.customers.where(c=>c.id==1);
db.Entry(customer).State = System.Data.EntityState.Detached; // add this
customer.name=santhosh;
customer.city=hyd;
db.SaveChanges();
That won't save your changes on name and city to database.
If you want something more robust(the above will fail an exception if the object was not yet attached), create a helper:
private static void Evict(DbContext ctx, Type t,
    string primaryKeyName, object id)
{           
    var cachedEnt =
        ctx.ChangeTracker.Entries().Where(x =>  
            ObjectContext.GetObjectType(x.Entity.GetType()) == t)
            .SingleOrDefault(x =>
        {
            Type entType = x.Entity.GetType();
            object value = entType.InvokeMember(primaryKeyName,
                                System.Reflection.BindingFlags.GetProperty,
null, x.Entity, new object[] { });
 
            return value.Equals(id);
        });
 
    if (cachedEnt != null)
        ctx.Entry(cachedEnt.Entity).State = EntityState.Detached;
}
To use: Evict(yourDbContextHere, typeof(Product), "ProductId", 1);
http://www.ienablemuch.com/2011/08/entity-frameworks-nhibernate.html

Can you give a little more of the surrounding code? Might be a little difficult without seeing how you are constructing your context.
This is how I typically handle updates (I hope it might give some insight):
using (var ctx = new myModel.myEntities())
{
int pollID = 1;
var poll = (from p in ctx.Polls
where p.PollID == pollID
select p).FirstOrDefault();
poll.Question = txtPoll.Text.Trim();
ctx.SaveChanges();
}

Jack Woodward, yours did not work for me.
I had to change it up a little for SQL Compact.
var customer= db.customers.AsNoTracking().Single(c=>c.id==1);
db.customers.Attach(customer);
customer.name=santhosh;
customer.city=hyd;
db.ObjectStateManager.ChangeObjectState(customer, System.Data.EntityState.Modified);
db.SaveChanges();
db.Dispose();
This worked alot better.

Related

The instance of the entity type cannot be tracked because another instance with the keyvalue is being tracked

I am basically trying to implement CRUD using EntityFrameWork core and .Net core 3.1. I have an issue with my update operation where I am not able update the context with the modified value.
I am using postman to initiate the request.
As you can see in the code below, I am trying to check if that customer exist and if it does pass the modified object to the context.
Function code
[FunctionName("EditCustomer")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous,"post", Route = "update-customer")] HttpRequest req)
{
var customer = JsonConvert.DeserializeObject<CustomerViewModel>(new StreamReader(req.Body).ReadToEnd());
await _repo.UpdateCustomer(customer);
return new OkResult();
}
Repository method
public async Task UpdateCustomer(CustomerViewModel customerViewModel)
{
if (customerViewModel.CustomerId != null)
{
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
if (customer == null)
{
throw new Exception("customer not found");
}
else
{
_context.Customers.Update(_mapper.Map<Customers>(customerViewModel));
await _context.SaveChangesAsync();
}
}
}
Mapping
public class CustomerManagerProfile : Profile
{
public CustomerManagerProfile()
{
CreateMap<CustomerDetails, CustomerDetailsViewModel>().ReverseMap();
CreateMap<CustomerOrders, CustomerOrdersViewModel>().ReverseMap();
CreateMap<CustomerOrderDetails, OrderDetailsViewModel>().ReverseMap();
CreateMap<Customers, CustomerViewModel>().ReverseMap();
}
}
Solution
public async Task UpdateCustomer(CustomerViewModel customerViewModel)
{
if (customerViewModel.CustomerId != null)
{
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
if (customer == null)
{
throw new Exception("customer not found");
}
else
{
var customerModel = _mapper.Map<Customers>(customerViewModel);
_context.Entry<Customers>(customer).State = EntityState.Detached;
_context.Entry<Customers>(customerModel).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
}
}
Entity Framework tracks your entities for you. For simplicity's sake, think of it like keeping a dictionary (for every table) where the dictionary key is equal to your entity's PK.
The issue is that you can't add two items of the same key in a dictionary, and the same logic applies to EF's change tracker.
Let's look at your repository:
var customer = _context
.Customers
.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId))
.FirstOrDefault();
The fetched customer is retrieved from the database and the change tracker puts it in his dictionary.
var mappedCustomer = _mapper.Map<Customers>(customerViewModel);
_context.Customers.Update();
I split your code in two steps for the sake of my explanation.
It's important to realize that EF can only save changes to tracked objects. So when you call Update, EF executes the following check:
Is this the same (reference-equal) object as one I have I my change tracker?
If yes, then it's already in my change tracker.
If not, then add this object to my change tracker.
In your case, the mappedCustomer is a different object than customer, and therefore EF tries to add mappedCustomer to the change tracker. Since customer is already in there, and customer and mappedCustomer have the same PK value, this creates a conflict.
The exception you see is the outcome of that conflict.
Since you don't need to actually track your original customer object (since EF doesn't do anything with it after fetching it), the shortest solution is to tell EF to not track customer:
var customer = _context
.Customers
.AsNoTracking()
.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId))
.FirstOrDefault();
Since customer is now not put into the change tracker, mappedCustomer won't cause a conflict anymore.
However, you don't actually need to fetch this customer at all. You're only interested in knowing whether it exists. So instead of letting EF fetch the entire customer object, we can do this:
bool customerExists = _context
.Customers
.Any(c => c.CustomerId.Equals(customerViewModel.CustomerId));
This also solves the issue since you never fetch the original customer, so it never gets tracked. It also saves you a bit of bandwidth in the process. It's admittedly negligible by itself, but if you repeat this improvement across your codebase, it may become more significent.
The most simple adjustment that you could make would be to avoid tracking your Customers on retrieval like this:
var customer = _context
.Customers
.AsNoTracking() // This method tells EF not to track results of the query.
.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId))
.FirstOrDefault();
It's not entirely clear from the code, but my guess is your mapper returns a new instance of Customer with the same ID, which confuses EF. If you would instead modify that same instance, your call to .Update() should work as well:
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
customer.Name = "UpdatedName"; // An example.
_context.Customers.Update(customer);
await _context.SaveChangesAsync();
As a matter of fact, if you track your Customer you don't even need to explicitly call .Update() method, the purpose of tracking is to be aware of what changes were made to the entities and should be saved to the database. Therefore this will also work:
// Customer is being tracked by default.
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
customer.Name = "UpdatedName"; // An example.
await _context.SaveChangesAsync();
EDIT:
The solution you yourself provide begins by tracking the results of your query (the Customer) instance, then stops tracking it (a.k.a. gets detached) before writing to database and instead starts tracking the instance that represents the updated Customer and also marks it as modified. Obviously that works as well, but is just a less efficient and elegant way of doing so.
As a matter of fact if you use this bizarre approach, I don't see the reason for fetching your Customer at all. Surely you could just:
if (!(await _context.Customers.AnyAsync(c => c.CustomerId == customerViewModel.CustomerId)))
{
throw new Exception("customer not found");
}
var customerModel = _mapper.Map<Customers>(customerViewModel);
_context.Customers.Update(customerModel);
await _context.SaveChangesAsync();
You use AutoMapper wrong way. It is not created to map from View model or DTO to Entity classes. It makes many problems and you are facing with only one of them now.
If you have more complex bussiness logic in you app (not just udpate all fields), it will be horrible to manage, test and debug what actually is happening in your code. You should write you own logic with some bussiness validation in case when you want to make some other update than CRUD.
If I were you I would create UpdateFields method in Customer class which would update them and finally call SaveChanges. It depends on whether you use anemic entity (anti)pattern or not. If you do not want your entity class to have any method you can create just method which manually map you VM do entity with some domain validation

Add or Update a Record?

I have an MVC application with the following code in the POST method of the controller. I am doing an EF Add and obviously that is not right. I want it to add the record if it doesn't exist, otherwise Update. How can I do that please?
try
{
AttributeEntities db = new AttributeEntities();
IEnumerable<string> items = viewModel.SelectedAttributes2;
int i = 0;
foreach (var item in items)
{
var temp = item;
// Save it
SelectedHarmonyAttribute attribute = new SelectedHarmonyAttribute();
attribute.CustomLabel = viewModel.ItemCaptionText;
attribute.IsVisible = viewModel.Isselected;
string harmonyAttributeID = item.Substring(1, 1);
// attribute.OrderNumber = Convert.ToInt32(order);
attribute.OrderNumber = i++;
attribute.HarmonyAttribute_ID = Convert.ToInt32(harmonyAttributeID);
db.SelectedHarmonyAttributes.Add(attribute);
db.SaveChanges();
}
}
You would need to check the database for the record you are trying to add/update. If the look-up returns null, that means that it doesn't exist in the database. If it does, you can modify the record that you looked up and call db.SaveChanges() to persist the changes you made to the database.
Edit:
int id = Convert.ToInt32(harmonyAttributeID);
var existingEntry = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == id);
One common way to determine an add or update is by simply looking at an identifier field, and setting the appropriate state.
using System.Data;
SelectedHarmonyAttribute attribute;
using (var db = new YourDbContext())
{
db.Entry(attribute).State = attribute.HarmonyAttribute_ID == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
You could import the System.Data.Entity.Migrations namespace and use the AddOrUpdate extension method:
db.SelectedHarmonyAttributes.AddOrUpdate(attribute);
db.SaveChanges();
EDIT:
I'm assuming that SelectedHarmonyAttributes is of type DbSet
EDIT2:
Only drawback with doing it this way (and it may not be a concern for you), is that your entity isn't responsible for it's own state change. This means that you can update any property of the entity to something invalid, where you might want to internally validate it on the entity itself or maybe do some other processing you always want to occur on update. If these things are a concern for you, you should add a public Update method onto the entity and check for its existence on the database first. e.g:
var attribute = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == harmonyAttributeID);
if (attribute != null)
{
attribute.Update(viewModel.ItemCaptionText, viewModel.Isselected, i++);
}
else
{
attribute = new Attribute(viewModel.ItemCaptionText, viewModel.Isselected);
db.SelectedHarmonyAttributes.Add(attribute);
}
db.SaveChanges();
Your update method might look something like:
public void Update(string customLabel, bool isVisible, int orderNumber)
{
if (!MyValidationMethod())
{
throw new MyCustomException();
}
CustomLabel = customLabel;
IsVisible = isVisible;
OrderNumber = orderNumber;
PerformMyAdditionalProcessingThatIAlwaysWantToHappen();
}
Then make all of the entities' properties public "get" but protected "set" so they can't be updated from outside the entity itself. This might be going off an a bit of a tangent but using the AddOrUpdate method would assume you don't want to control the way an update occurs and protect your domain entity from getting into an invalid state etc. Hope this helps!

Update database via Linq to SQL not working

I'm developing a C# ASP.NET application, in which i'm retrieving some data from the database, throwing in a form, and when i click on Save, i want it to save my changes in the database.
I'm using Linq to SQL. The code below, at the end, call the method ClienteBusiness.SalvarAlteracoes(cliente), which by the way, only calls the ClienteData.SalvarAlteracoes(cliente) method.
protected void Salvar()
{
TB_CLIENTE_CLI cliente = new TB_CLIENTE_CLI();
int idEstado = 0;
int idCidade = 0;
if (!Int32.TryParse(ddlEstado.SelectedValue, out idEstado))
{
return;
}
if (!Int32.TryParse(Request.Form[ddlCidade.UniqueID], out idCidade))
{
return;
}
cliente.TXT_RAZAOSOCIAL_CLI = txtRazaoSocial.Text;
cliente.TXT_NOMEFANTASIA_CLI = txtNomeFantasia.Text;
cliente.TXT_CNPJ_CLI = txtCNPJ.Text;
cliente.TXT_CEP_CLI = txtCEP.Text;
/*e os demais campos*/
//Se a tela for de edição, altera o valor do ID para o cliente correspondente.
cliente.ID_CLIENTE_CLI = this.IdCliente;
ClienteBusiness.SalvarAlteracoes(cliente);
HTMLHelper.jsAlertAndRedirect(this, "Salvo com sucesso!", ResolveUrl("~/Pages/ClientePage.aspx"));
}
The method which save the changes is described below:
public static Int32 SalvarAlteracoes(TB_CLIENTE_CLI cliente)
{
using (PlanoTesteDataContext context = DataContext.ObterConexao())
{
if (cliente.ID_CLIENTE_CLI == 0)
{
context.TB_CLIENTE_CLIs.InsertOnSubmit(cliente);
}
else
{
context.TB_CLIENTE_CLIs.Attach(cliente, true);
}
context.SubmitChanges();
} return cliente.ID_CLIENTE_CLI;
}
On the line context.TB_CLIENTE_CLIs.Attach(cliente, true); i'm receiving a System.InvalidOperationException: An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
I've already checked the UpdateChecks and they are set to Never.
What can I do? Thanks and sorry for the bad english.
This should work:
else
{
context.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, cliente);
context.TB_CLIENTE_CLIs.Attach(cliente);
}
This Refresh overload will keep the changes made by the user,it compares the modified entity with the original values from the database, detects the difference and marks the entity as modified and the call to SubmitChanges applies the update to the database.
You may very well run into trouble using Linq2SQL with disconnected entities. EF is a more suited solution to handle this.
However, please ensure you have set all properties on the entity on UpdateCheck to NEVER. I have tested this myself and it works. If this does work it will run an UPDATE statement on every column regardless of whether it has been updated or not. Could cause a problem if you use triggers on your tables. It might be a better idea to use a Timestamp instead to track the entities so concurrency issues between multiple users can be raised.
If you try to Attach an entity from a context where the ObjectTrackingEnabled is not set to False then you will have the following exception thrown:
An unhandled exception of type 'System.NotSupportedException' occurred in System.Data.Linq.dll
Additional information: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
As an example please use the following for retrieving and reattaching an entity:
public TB_CLIENTE_CLI Get(int id)
{
using (PlanoTesteDataContext ctx = new PlanoTesteDataContext())
{
ctx.ObjectTrackingEnabled = false;
return ctx.TB_CLIENTE_CLI.SingleOrDefault(n => n.ID == id);
}
}
public void Save(TB_CLIENTE_CLI cliente)
{
using (PlanoTesteDataContext ctx = new PlanoTesteDataContext())
{
ctx.DeferredLoadingEnabled = false;
ctx.TB_CLIENTE_CLI.Attach(cliente, true);
ctx.SubmitChanges();
}
}
You will also need to set DeferredLoadingEnabled loading to False in the Save method so that you can save down changes on the entity subsequent times after the first initial save on modification.

Controller method not updating(same result every time)

I have following method in my mvc controller:
[HttpGet]
public ActionResult UserProfile(String username)
{
var user = db.Users.Find(username);
return View(user);
}
This function returns View with user profile. But result of this is the same, regardless of changes in database.
When I debug it seems like db is not changing at all, while in other controllers everything works just fine.
EDIT:
Place when I make changes
public ActionResult ExecuteRetreive(String username, String ISBN)
{
if (IsValid(username))
{
var resBook = db.Books.Find(ISBN);
var resUser = db.Users.Find(username);
var resRentedBooks = (from rb in db.RentedBooks
join b in db.Books on rb.ISBN equals b.ISBN
where b.ISBN == ISBN
where rb.Login == username
where rb.Returned == null
select rb).FirstOrDefault();
if (resRentedBooks == null)
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "" });
}
resRentedBooks.Returned = DateTime.Now;
resBook.IsRented = false;
resUser.RentedBooks--;
db.SaveChanges();
return RedirectToAction("Success", "FailSuccess");
}
else
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "Niepoprawna nazwa użytkownika" });
}
}
Im new to this so dont laugh at my code :P When I display resUser.RentedBooks--; it is the same every time.
As a follow up to what #JeroenVannevel said in the comments, another problem that you might be having because you're using a static context (and one that I've had to deal with in the past) is that once a specific DbContext has loaded an entity (or a set of entities, in my case) it won't tend to refresh just because some outside changes were made in the database. It loads those entities into Local and just refers to those automatically if you query for it.
The solution, then, is to always put your DbContext calls wrapped up in a using block, since DbContext implements IDisposable.
One word of caution with this approach, since you're using MVC: If you are using lazy loading, and you know that your View will need some information from a child object (or to list the names of a collection of child objects), you will absolutely need to hydrate those child entities before you get out of the using block, or you will find yourself getting exceptions saying that your context has been disposed.

Entity Framework SaveChanges() not updating the database

var paymentAttempt = _auctionContext.PaymentAttempts.Where(o => o.Id == paymentAttemptId).SingleOrDefault();
if (paymentAttempt != null)
{
paymentAttempt.PaymentAttemptStatusId = (int)PaymentAttemptStatus.Defunct;
paymentAttempt.PaymentAttemptStatus = _auctionContext.PaymentAttemptStatuses.Where(pas => pas.Id == paymentAttempt.PaymentAttemptStatusId).First();
var relevantWinningBidsTotalPrices = _auctionContext.GetWinningBidsTotalPricesForPaymentAttempt(paymentAttemptId).ToArray();
foreach (var winningBid in relevantWinningBidsTotalPrices)
{
winningBid.Locked = false;
_auctionContext.UpdateObject(winningBid);
}
_auctionContext.SaveChanges();
}
In the above code after
_auctionContext.SaveChanges();
is called winningBid is updated as expected but paymentAttempt isn't. Why is this? It is really frustrating. There is no error either. I would expect a failure to occur if there was a problem like EF wasn't tracking the object or something like that, but no such error is happening.
That's because you need to pass the paymentAttempt object to your context, to let it know that it is an object that needs to be updated.
For example, assuming that _auctionContext is an instance of DbContext:
// any changes related to the paymentAttempt object
_auctionContext.Entry(paymentAttempt).State = EntityState.Modified;
foreach (var winningBid in relevantWinningBidsTotalPrices)
{
winningBid.Locked = false;
_auctionContext.UpdateObject(winningBid);
}
_auctionContext.SaveChanges();
Another option is the Attach method:
_auctionContext.Attach(paymentAttempt);
_auctionContext.ObjectStateManager.ChangeObjectState(paymentAttempt, System.Data.EntityState.Modified);
If you don't have Entry try adding:
using System.Data.Entity.Infrastructure;
using System.Data.Entity;
then you may simply use:
_auctionContext.Entry(paymentAttempt).State = EntityState.Modified;
_auctionContext.SaveChanges();
I fell on this question but for a different problem. I discovered that if you call SaveChanges() on an object that hasn't been modified, EF will not update anything. This makes sense, but I needed the DB to be updated so that other users would see that a SaveChanges() had been executed, regardless of whether any fields had changed. To force an update without changing any fields:
Dim entry As DbEntityEntry = entities.Entry(myentity)
entry.State = Entity.EntityState.Modified
I know this is late but there's another explanation worth mentioning. Even though your field name contains ID and may be set to autoincrement, be sure to verify that you declared it in your table the primary key.

Categories