Entity Framework (6.1.3) updates not reflected in the DataContext - c#

1) I add a new entity to the context and call _context.SaveChanges(). Entity is added as expected
2) I update the same entity and call _context.SaveChanges() I put a break point immediately after and inspect the _context and my update is reflected on the entity AND is indeed saved to the DB.
3) I call _context.Set< T >().ToList() later in my code base and the update is NOT reflected on the entity. (_context does not reflect the updated value at this point)
What could possibly be the cause and what can I do to resolve? Any help would be greatly appreciated
Responding to request for code..
From the repository...
public List<T> GetAll()
{
return _context.Set<T>().ToList();
}
public void SaveChanges()
{
_context.SaveChanges();
var xxx = _context.Customers.ToList();
}
From the call to get all...
var customersToUpdate = _customerManager.GetAllCustomers();
From the CustomerManager...
public List<Customer> GetAllCustomers()
{
return _customerRepository.GetAll();
}
Pretty basic stuff.

Are you sure that you use only one instance of the _customerRepository? And that every time you update the _context you do it in the same context under _customerRepository ? It sounds like you have multiple instance, one get updated while the others dont

Basically you need to tell EF that you updated something.
Easiest way of doing that is:
var customer = _context.Customers.First();
customer.Name = "new name";
_context.Entry(customer).State = EntityState.Modified;
_context.SaveChanges();
Or you can be more specific of what is changed like below:
customer.Name = "new name";
context.Entry(customer).Property(u => u.Name).IsModified = true;
_context.SaveChanges();
You can enable automatic changes detection like that:
context.Configuration.AutoDetectChangesEnabled = true;
In this case
DbSet.Find
DbSet.Local
DbSet.Remove
DbSet.Add
DbSet.Attach
DbContext.SaveChanges
DbContext.GetValidationErrors
DbContext.Entry
DbChangeTracker.Entries
all will automatically detect changes.

Related

Entity Update Issue | C# Entity Framework

I have landed up in a situation here.
I am loading the User from a custom function here :
public User GetUserById(int Id)
{
var User = DBContext.Users.SingleOrDefault(x=>x.Id == Id);
return User;
}
The problem is in the controller where I call this!
var User = GetUserById(Id);
User.Language = UserModel.Language;
//UpdateModel(User); //I tried this but not working.
DBContext.SaveChanges();
The database is not being updated.
I tried loading the User in the controller directly with linq and things go fine.
But, Why is it not working the other way?
Is there any workaround to make it work with this function?
Isn't there any function like DBContext.Users.Update(User). I remember using something similar, but am not able to recollect!
As EBrown said, they need to be the same instance of DbContext. I'm betting your service uses one context and the controller another. So now when it gets to the controller it is disconnected and not tracked by EF.
The solution is to reconnect it to the context used by the controller (there are other ways to achieve this as well).
var User = GetUserById(Id); // returns a disconnected user
User.Language = UserModel.Language;
// Attach back to context and tell EF it is updated
DBContext.Users.Attach(User);
DBContext.Entity(User).State=EntityState.Modified;
DBContext.SaveChanges();
If this is your postback code, you could just as well write aUserUpdate function:
public void UpdateUser(UserModel userViewModel)
{
var userEntity = DBContext.Users.Find(userViewModel.Id); // Get your user from database
Mapper.Map(userViewModel, userEntity); // Use Automapper to move the changed fields into your entity
DbContext.SaveChanges();
}
Then your controller POST is simply:
if (ModelState.IsValid)
{
UpdateUser(UserModel);
// redirect to list or where ever...
}

EF: how to modify existing entry in DB in N-layered application

Im not EF specialist propably, but I have an issue with it:).
Im doing n-layered business application, so I have Service code and Repository code in my app. In my service code, it read existing User entity, I do update of some properties and it calls Repositiry' method Edit. And there error appears:
Attaching an entity of type
'MobileWallet.Common.Repository.MwbeUserData' failed because another
entity of the same type already has the same primary key value. This
can happen when using the 'Attach' method or setting the state of an
entity to 'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate.
My Edit method looks like this:
public override void Edit(MwbeUserData entityToUpdate)
{
LogChangeTrackerStateValues("UserUpdate starts");
if (Context.Entry(entityToUpdate).State == EntityState.Detached)
{
DbSet.Attach(entityToUpdate);
}
Context.Entry(entityToUpdate).State = EntityState.Modified;
//fix for User.Address problem
Context.Entry(entityToUpdate.Address).State = EntityState.Modified;
LogChangeTrackerStateValues("UserUpdate ends");
}
I also tried code like this:
public override void Edit(MwbeUserData entityToUpdate)
{
Context.Entry(entityToUpdate).State = EntityState.Modified;
//fix for User.Address problem
Context.Entry(entityToUpdate.Address).State = EntityState.Modified;
}
Record which it being udpated is kept in ChangeTracker
Context.ChangeTracker.Entries().ToList()[1]
but Context.Entry(entityToUpdate).State = Detach for this object.
QUESTION 1: How to solve this?
QUESTION 2: Any good tutorial to understand how to work with EF with business n-layered applications?
Thanks for answers.
UPDATE 1:
New findings:
In Repository Edit method:
Context.ChangeTracker.Entries().ToList()[1].CurrentValues["Firstname"] = "AAA"
Context.ChangeTracker.Entries().ToList()[1].OriginalValues["Firstname"]= "AAA"
but CurrentValue should be BBB, why is not updated? it was updated in Service code which calls Respority Code and updated entity is passed to Repository Edit method.
UPDATE 2:
More about my architecture: I have 3 layes Controler(WEB API), Service and Repository. So my Service method update looks like this:
public bool UpdateUser(MwbeUserUpdateIn userUpdateData)
{
MwbeReturnData<MwbeUserData> userData = repository.Get(userUpdateData.UserId);
// Determine if user exists
if (MwbeResponseCodes.NotFound == userData.Code)
{
return false;
}
MwbeUserData user = userData.Data;
// Check each field to be updated
if (!String.IsNullOrEmpty(userUpdateData.FirstName))
{
user.Firstname = userUpdateData.FirstName;
}
if (!String.IsNullOrEmpty(userUpdateData.MiddleName))
{
user.Middlename = userUpdateData.MiddleName;
}
if (!String.IsNullOrEmpty(userUpdateData.LastName))
{
user.Secondname = userUpdateData.LastName;
}
if (!String.IsNullOrEmpty(userUpdateData.MobileNumber))
{
user.Mobilenumber = userUpdateData.MobileNumber;
}
if (!String.IsNullOrEmpty(userUpdateData.Email))
{
user.Email = userUpdateData.Email;
}
if (null != userUpdateData.BirthDate)
{
user.BirthDate = (DateTime)userUpdateData.BirthDate;
}
// Update Addres fields
if (null != userUpdateData.Address)
{
if (!String.IsNullOrEmpty(userUpdateData.Address.City))
{
user.Address.City = userUpdateData.Address.City;
}
if (!String.IsNullOrEmpty(userUpdateData.Address.Country))
{
user.Address.Country = userUpdateData.Address.Country;
}
if (!String.IsNullOrEmpty(userUpdateData.Address.Street))
{
user.Address.Street = userUpdateData.Address.Street;
}
if (!String.IsNullOrEmpty(userUpdateData.Address.ZipCode))
{
user.Address.ZipCode = userUpdateData.Address.ZipCode;
}
}
// Save changes to DB
repository.Edit(ref user);
repository.SaveChanges();
return true;
}
Instead of calling Context.Entry(entityToUpdate) and setting it state to modified you could search for the entity you want to update and modify its members. Then set its state as modified, like this.
Also in your Edit function you should explicitly list which members of your MwbeUserData object your are updating. This is a security issue, it will prevent someone over posting extra members to your controller.
Here is an example of a edit function I have used.
public ActionResult Edit([Bind(Include = "Id,CompanyName,Abbreviation,CompanyTypeRef")] Company company)
{
if (!ModelState.IsValid) return View(company);//error invalid state
var c = Entities.Set<Company>().FirstOrDefault(x => x.Id == company.Id);
if(c == null) return View(company);// error could not find entity
Entities.Entry(c).CurrentValues.SetValues(company);
Entities.Entry(c).State = EntityState.Modified;
Entities.SaveChanges();
return RedirectToAction("Edit", "Company", new { id = company.Id });
}
Update:
I had a similar issue with entity changes not being recorded by changes tracker in EntityFramework.Extented and it was fix by updating the entities CurrentValues with this line.
Entities.Entry(c).CurrentValues.SetValues(company);

Updating existing data in EF 6 throws exception - "...entity of the same type already has the same primary key value."

I am trying to update a record using Entity Framework 6, code-first, no fluent mapping or a tool like Automapper.
The entity(Employee) has other composite properties associated with it like Addreess(collection), Department
It is also inherited from a base called User
The save method is as follows, with _dbContext being the DbConext implementation
public bool UpdateEmployee(Employee employee)
{
var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault();
if (entity == null)
{
_dbContext.Employees.Add(employee);
}
else
{
_dbContext.Entry(employee).State = EntityState.Modified; // <- Exception raised here
_dbContext.Employees.Attach(employee);
}
return _dbContext.SaveChanges() > 0;
}
I keep getting the error:
Attaching an entity of type failed because another entity of the same
type already has the same primary key value. This can happen when
using the 'Attach' method or setting the state of an entity to
'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate.
I have tried the following:
Attaching before setting to EntityState.Modified
Adding AsNoTracking() on querying if the object exists(No exception but DB is not updated) - https://stackoverflow.com/a/23228001/919426
Saving using the base entity _dbContext.Users instead of the Employee entity - https://stackoverflow.com/a/25575634/919426
None of which is working for me now.
What could I have gotten wrong for some of those solutions not to work in my situation?
EF already includes a way to map properties without resorting to Automapper, assuming you do not have navigation properties to update:
public bool UpdateEmployee(Employee employee)
{
var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault();
if (entity == null)
{
_dbContext.Employees.Add(employee);
}
else
{
_dbContext.Entry(entity).CurrentValues.SetValues(employee);
}
return _dbContext.SaveChanges() > 0;
}
This usually generates a better SQL statement since it will only update the properties that have changed.
If you still want to use the original method, you'll get rid of entity from the context, either using AsNoTracking (not sure why it didn't update in your case, it should have no effect, so the problem might be something else) or as modifying your query to prevent it from materializing the entity in the first place, using something like bool exists = dbContext.Employees.Any(c => c.Id == employee.Id) for example.
This worked for myself
var aExists = _db.Model.Find(newOrOldOne.id);
if(aExists==null)
{
_db.Model.Add(newOrOldOne);
}
else
{
_db.Entry(aExists).State = EntityState.Detached;
_db.Entry(newOrOldOne).State = EntityState.Modified;
}
I've encountered the same thing when using a repository and unit of work pattern (as documented in the mvc4 with ef5 tutorial).
The GenericRepository contains an Update(TEntity) method that attempts to Attach then set the Entry.State = Modified. The up-voted 'answer' above doesn't resolve this if you are going to stick to the uow / repo pattern.
I did attempt to use the detach process prior to the attach, but it still failed for the same reason as indicated in the initial question.
The reason for this, it turns out, is that I was checking to see if a record existed, then using automapper to generate an entity object from my dto prior to calling update().
By checking for the existance of that record, i put the entity object in scope, and wasn't able to detach it (which is also the reason the initial questioner wasn't able to detach)... Tt tracked the record and didn't allow any changes after I automapper'ed the dto into an entity and then attempted to update.
Here's the generic repo's implementation of update:
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
This is my PUT method (i'm using WebApi with Angular)
[HttpPut]
public IHttpActionResult Put(int id, Product product)
{
IHttpActionResult ret;
try
{
// remove pre-check because it locks the record
// var e = unitOfWork.ProductRepository.GetByID(id);
// if (e != null) {
var toSave = _mapper.Map<ProductEntity>(product);
unitOfWork.ProductRepository.Update(toSave);
unitOfWork.Save();
var p = _mapper.Map<Product>(toSave);
ret = Ok(p);
// }
// else
// ret = NotFound();
}
catch (DbEntityValidationException ex)
{
ret = BadRequest(ValidationErrorsToMessages(ex));
}
catch (Exception ex)
{
ret = InternalServerError(ex);
}
return ret;
}
As you can see, i've commented out my check to see if the record exists. I guess i'll see how it works if I attempt to update a record that no longer exists, as i no longer have a NotFound() return opportunity.
So to answer the initial question, i'd say don't look for entity==null before making the attempt, or come up with another methodology. maybe in my case, i could dispose of my UnitOfWork after discovery of the object and then do my update.
You need to detach to avoid duplicate primary key exception whist invoking SaveChanges
db.Entry(entity).State = EntityState.Detached;

Confused about attaching/detaching entities in EF

Using ASP.NET MVC and Entity Framework, I encounter some attach/detach errors when I need it to write changes to the database.
First here's how I get the records from my repository:
public PublishedApplication GetPublishedApplication(int id)
{
return dal.ExecuteFirstOrDefault(
context.PublishedApplication.Include("Customers")
.Where(w => w.Id == id));
}
Note that this is not being detached, and it has MergeOption = AppendOnly.
Another place, I call a method in my repo to attach a customer to the application:
public void AttachCustomer(int applicationId, int customerId)
{
var app = new PublishedApplication{ Id = applicationId};
var customer = new Customer { Id = customerId};
try
{
context.AttachTo("PublishedApplication", app);
context.AttachTo("Customer ", customer );
app.Customers.Add(customer);
context.SaveChanges();
}
catch
{
throw;
}
}
This, of course, gives me an error that the objects (app and customer) are already attached to an object context.
So I need to detach my objects in my repository. This is also more correct as I don't need to throw "query related information" around the web. But how can I do this? If I detach the entity in my GetPublishedApplication method, my graph of related data is lost, and I can't afford that. I need that graph elsewhere on my site. Can't use merge option NoTracking either.
So what options do I have?
Thanks in advance!
I think, there isn't easy solution for reattaching entities.
May be this post will help you:
Reattaching Entity Graphs with the Entity Framework on codeproject.com

Linq2Sql: Manage DataContext

In the following code doesn't work as
public void Foo()
{
CompanyDataContext db = new CompanyDataContext();
Client client = (select c from db.Clients ....).Single();
Bar(client);
}
public void Bar(Client client)
{
CompanyDataContext db = new CompanyDataContext();
db.Client.Attach(client);
client.SomeValue = "foo";
db.SubmitChanges();
}
This doens't work, I get error msg. "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."
How do you work with DataContexts throughout an application so you don't need to pass around a reference?
What
They really mean it with 'This is not supported.'. Attaching to an object fetched from another data context is not implemented.
There are a number of workarounds to the problem, the recommended way is by serializing objects, however this is not easy nor a clean approach.
The most simple approach I found is to use a readonly DataContext for fetching objects like this:
MyDataContext dataContext = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
The objects obtained from this context can be attached to another context but only applies to some scenarios.
The PLINQO framework generates detach for all entities making it easy to detach and reattach objects without receiving that error.
public void Foo()
{
CompanyDataContext db = new CompanyDataContext();
Client client = (select c from db.Clients ....).Single();
// makes it possible to call detach here
client.Detach();
Bar(client);
}
public void Bar(Client client)
{
CompanyDataContext db = new CompanyDataContext();
db.Client.Attach(client);
client.SomeValue = "foo";
db.SubmitChanges();
}
Here is the article that describing how the detach was implemented.
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx
Yep. That's how it works.
You have tagged this asp.net so I guess it's a web app. Maybe you want one datacontext per request?
http://blogs.vertigo.com/personal/keithc/Blog/archive/2007/06/28/linq-to-sql-and-the-quote-request-scoped-datacontext-quote-pattern.aspx
(P.S. It's a lot harder in WinForms!)
I've created data access classes that encapsulate all the communication with Linq2Sql.
These classes have their own datacontext that they use on their objects.
public class ClientDataLogic
{
private DataContext _db = new DataContext();
public Client GetClient(int id)
{
return _db.Clients.SingleOrDefault(c => c.Id == id);
}
public void SaveClient(Client c)
{
if (ChangeSetOnlyIncludesClient(c))
_db.SubmitChanges();
}
}
Ofcourse you will need to keep this object instantiated as long as you need the objects.
Checking if only the rigth object has been changed is altso somewhat bothersom, you could make methods like
void ChangeClientValue(int clientId, int value);
but that can become a lot of code.
Attaching and detaching is a somewhat missing feature from Linq2Sql, if you need to use that a lot, you sould probably use Linq2Entities.
I took a look at this and found that it appears to work fine as long as the original DataContext has been disposed.
Try wrapping the DataContext with using() and make sure your changes occur after you've attached to the second DataContext? It worked for me..
public static void CreateEntity()
{
User user = null;
using (DataClassesDataContext dc = new DataClassesDataContext())
{
user = (from u in dc.Users
select u).FirstOrDefault();
}
UpdateObject(user);
}
public static void UpdateObject(User user)
{
using (DataClassesDataContext dc = new DataClassesDataContext())
{
dc.Users.Attach(user);
user.LastName = "Test B";
dc.SubmitChanges();
}
}
You need to handle object versioning.
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.
So, if there's no timestamp member or other 'versioning' mechanism provided there's no way for LINQ to determine whether that data has changed - hence the error you are seeing.
I resolved this issue by adding a timestamp column to my tables but there are other ways around it. Rick Strahl has written some decent articles about exactly this issue.
Also, see this and this for a bit more info.

Categories

Resources