Entity Framework 5.0 db.savechange not working - c#

When I try to update my entry in update function it's execute successfully but database not updated.
Please find the following code
public static string UpdateEmployee(Employee employee)
{
using (var db = new RandDEntities())
{
var empObj = db.Employees.First(x => x.EmpID == employee.EmpID);
db.Entry(empObj).State = EntityState.Modified;
db.SaveChanges();
}
return "";
}

The problem is that you're saving the wrong entity (i.e. empObj) instead of the entity that has the changes (i.e. employee). Your code pulls empObj out of the database, and then turns around and saves it, without making any changes to it. You need to modify your code as follows:
public static string UpdateEmployee(Employee employee)
{
using (var db = new RandDEntities())
{
db.Employees.Attach(employee);
db.Entry(employee).State = EntityState.Modified;
db.SaveChanges();
}
return "";
}

That has absolutely nothing to do with Entity Framework but more with the fact that you likely use localdb and your database is not where you think it is, and reinitialized (reset) to empty every time you press start.
This question, in dozens of variants, gets asked over and overy by people not bothering to check where they look (and then realizing the database files have different paths).

Related

EF code first update entity with its children

I got update method that updates only the entity without its children
public void Update(T obj)
{
Ensure.IsNull(obj);
using (var DB = AccessManager.db)
{
DB.Set<T>().Attach(obj);
DB.Entry(obj).State = System.Data.EntityState.Modified;
DB.SaveChanges();
}
}
I try to update
LessonModel.Title = "Updated";
LessonModel.Files.Add(new LessonFileModel { IdxNumber = LessonModel.IdxNumber, FileName = "asd", FilePath = " asdf" });
DALFacade.Lesson.Update(LessonModel);
Only the title gets updated. The files are not updated.
So if the method like this:
public void Update(LessonModel obj)
{
Ensure.IsNull(obj);
using (var DB = AccessManager.db)
{
DB.Lessons.Attach(obj);
DB.Entry(obj).State = System.Data.EntityState.Modified;
DB.SaveChanges();
}
}
How can i save the child?
This is one of the problems that arise when you use the "Generic Repository" anti pattern. Writing an Update method that works for every combination of entity graphs that you pass in and will always do exactly what you want it to do is a big pain in the EF.
Instead, try writing repositories that match your use cases.
Your current problem could be solved if you added the files to the lesson after you attached it to the context. Marking an entry as modified marks all properties on this entry as modified, it doesn't affect relationships.

Not able to retrieve OriginalValues in Entity Framework 5

I am writing a asp.net mvc4 app and I am using entity framework 5. Each of my entities have fields like EnteredBy, EnteredOn, LastModifiedBy and LastModifiedOn.
I am trying to auto-save them by using the SavingChanges event. The code below has been put together from numerous blogs, SO answeres etc.
public partial class myEntities : DbContext
{
public myEntities()
{
var ctx = ((IObjectContextAdapter)this).ObjectContext;
ctx.SavingChanges += new EventHandler(context_SavingChanges);
}
private void context_SavingChanges(object sender, EventArgs e)
{
ChangeTracker.DetectChanges();
foreach (ObjectStateEntry entry in
((ObjectContext)sender).ObjectStateManager
.GetObjectStateEntries
(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
CurrentValueRecord entryValues = entry.CurrentValues;
if (entryValues.GetOrdinal("LastModifiedBy") > 0)
{
HttpContext currContext = HttpContext.Current;
string userName = "";
DateTime now = DateTime.Now;
if (currContext.User.Identity.IsAuthenticated)
{
if (currContext.Session["userId"] != null)
{
userName = (string)currContext.Session["userName"];
}
else
{
userName = currContext.User.Identity.Name;
}
}
entryValues.SetString(
entryValues.GetOrdinal("LastModifiedBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("LastModifiedOn"), now);
if (entry.State == EntityState.Added)
{
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), now);
}
else
{
string enteredBy =
entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy"));
DateTime enteredOn =
entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn"));
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"),enteredBy);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), enteredOn);
}
}
}
}
}
}
My problem is that entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy")) and entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn")) are not returning the original values but rather the current values which is null. I tested with other fields in the entity and they are returning the current value which were entered in the html form.
How do I get the original value here?
I think the problem may be that you are using the instance provided by the model binder as the input to your controller method, so EF does not know anything about that entity and its original state. Your code may look like this:
public Review Update(Review review)
{
_db.Entry(review).State = EntityState.Modified;
_db.SaveChanges();
return review;
}
In that case, EF knows nothing about the Review instance that is being saved. It is trusting you and setting it as modified, so it will save all of its properties to the database, but it does not know the original state\values of that entity.
Check the section named Entity States and the Attach and SaveChanges Methods of this tutorial. You can also check the first part of this article, that shows how EF does not know about the original values and will update all properties in the database.
As EF will need to know about the original properties, you may first load your entity from the database and then update its properties with the values received in the controller. Something like this:
public Review Update(Review review)
{
var reviewToSave = _db.Reviews.SingleOrDefault(r => r.Id == review.Id);
//Copy properties from entity received in controller to entity retrieved from the database
reviewToSave.Property1 = review.Property1;
reviewToSave.Property2 = review.Property2;
...
_db.SaveChanges();
return review;
}
This has the advantage that only modified properties will be send and updated in the database and that your views and view models don't need to expose every field in your business objects, only those that can be updated by the users. (Opening the door for having different classes for viewModels and models\business objects). The obvious disadvantage is that you will incur an additional hit to the database.
Another option mentioned in the tutorial I referenced above is for you to save the original values somehow (hidden fields, session, etc) and on save use the original values to attach the entity to the database context as unmodified. Then update that entity with the edited fields. However I would not recommend this approach unless you really need to avoid that additional database hit.
Hope that helps!
I was running into a similar problem when trying to audit log the Modified values of an Entity.
It turns out during the post back the ModelBinder doesn't have access to the original values so the Model received is lacking the correct information. I fixed my problem by using this function which clones the current values, relods the object, and then reset the current values.
void SetCorrectOriginalValues(DbEntityEntry Modified)
{
var values = Modified.CurrentValues.Clone();
Modified.Reload();
Modified.CurrentValues.SetValues(values);
Modified.State = EntityState.Modified;
}
You can gain access to the DbEntityEntry though the change tracker, or the entry function from your context.

Lazy Loading and multiple references

My Database is set up with an Entity table, which has a Ref_Type navigation property (and a FK which references TypeID). The Type table, has a Ref_Department, same FK setup. Finally, the Department table has a Ref_Locale and same FK setup.
I save the Entities in a List, which is a property of a singleton. It is created as follows;
private Singleton()
{
using (Database db = new Database())
{
List<Entities> EntityList = db.Entities.ToList<Entities>();
}
}
This is fine, the navigation properties are ALL loaded and I can access any one of them.
The problem comes when I update an Entity entry as follows;
public void UpdateEntity(Entities oldEnt, Entities newEnt)
{
using (Database db = new Database())
{
Entities ent = db.Entities.Where(e => e.EntityName == oldEnt.EntityName).FirstOrDefault();
ent.EntityName = newEnt.EntityName;
ent.EntityEmail = newEnt.EntityEmail;
...
ent.EntityType_ID = newEnt.EntityType_ID;
db.SaveChanges();
}
RefreshEntities();
}
public void RefreshEntities()
{
using (Database db = new Database())
{
db.Configuration.LazyLoadingEnabled = false;
db.SaveChanges();
EntityList = db.Entities.Include("Ref_EntityType").Include("Ref_EntityPosition").ToList<Entities>();
}
}
Ref_Entity gets loaded properly, but then within Ref_Entity, Ref_Department is just null. I've tried just using db.Entities.ToList<Entities>(); like in my constructor, no dice. As you can see, I've also tried turning LazyLoading off (I thought I might need to call SaveChanges() for it to actually apply the flag). I've also tried .Include("Ref_Department") but it just complains that it doesn't exist for Entities, which makes sense.
The newEnt that I pass to the UpdateEntity method does not have Ref_Type initialised, I'm working under the assumption that anything not changed in the UpdateEntity method would just stay the same...
So now I'm at a bit of a loss as to what's going on and how to fix it. If anyone could help explain where I'm going wrong or give me some pointers about how to fix my code to make it work, that would be great.
On a whim, I modified RefreshEntities() to;
EntityList = db.Entities.Include("Ref_EntityPosition").Include("Ref_EntityType").
Include("Ref_EntityType.Ref_Department").
Include("Ref_EntityType.Ref_Department.Ref_Locale").ToList<Entities>();
And now I'm getting all the references.
I'm still not sure why it would load all the references in the constructor but not in the RefreshEntities() method, even if the calls are identical, but this solves the problem so I'm happy enough to leave it like that.

Multiple instances of IEntityChangeTracker Error

I've read through at least a dozen other questions just like this one, but I am having trouble grasping some of this stuff.
I'm used to developing ASP.NET MVC3 with repositories and code-first entities linking to the entity framework.
I've recently switched to database-first ADO.NET with services development.
I find this to be very clean since I can access stuff through my foreign keys.
Anyway, my old save methods seem to be broken since I constantly get this error
An entity object cannot be referenced by multiple instances of
IEntityChangeTracker
So here's a look at my save action and my service:
Action:
[HttpPost]
public ActionResult AddReview(Review review, int id)
{
User loggedInUser = userService.GetUserByusername(User.Identity.Name);
review.WriterId = loggedInUser.UserId;
review.ProductId = id;
if (ModelState.IsValid)
{
reviewService.Save(review);
Product product = productService.GetProduct(id);
if(product.Quantity>=1)
product.Quantity--;
product.TimesBought++;
productService.UpdateRating(product, reviewService);
loggedInUser.GoldCoins -= product.Price;
Session["goldCoins"] = loggedInUser.GoldCoins;
userService.Save(loggedInUser);
productService.Save(product);
}
else
{
return View(review);
}
return RedirectToAction("Index", "Answers", new { reviewId = review.ReviewId });
Service:
public class ReviewService : Service<Review, CapstoneEntities>
{
...
public void Save(Review review)
{
using (var db = new CapstoneEntities())
{
if (review.ReviewId == 0)
{
db.Reviews.Add(review);
db.Entry(review).State = EntityState.Added;
}
else
{
db.Entry(review).State = EntityState.Modified;
}
db.SaveChanges();
}
}
}
My suspicion is with this line of code: using (var db = new CapstoneEntities()) but I'm not sure how else to do this. Again, this worked perfectly with my old way of doing things but now I get errors on just about ever CRUD operation.
Thank you.
It looks like this is being caused by having an entity belong to multiple DataContexts. Whatever code that is calling that action should use the same DataContext to create the entity as the one used to persist it to the datastore.
In most instances you should only keep one instance of the DataContext. You can use a DI framework like Castle to define/store a dependency (in this case the DataContext) as Transient or PerWebRequest and inject it into the service and controller, so you'll always have a reference to the same instance of the DataContext.
I am new to MVC & Entity frame work. I got same problem after fighting a lot
This solution worked for me. Hope it can be useful for you guys.
var mediaItem = db.MediaItems.FirstOrDefault(x => x.Id == mediaItemViewModel.Id);
mediaItem.Name = mediaItemViewModel.Name;
mediaItem.Description = mediaItemViewModel.Description;
mediaItem.ModifiedDate = DateTime.Now;
mediaItem.FileName = mediaItem.FileName;
mediaItem.Size = KBToMBConversion(mediaItemViewModel.Size);
mediaItem.Type = mediaItem.Type;
//db.Entry(mediaItem).State = EntityState.Modified;// coment This line
db.SaveChanges();
Cause you are reading the the whole object from db and holding it in the current context and when you try to modify the entity state its tells you already one entity attached to the current context. just call save changes it will save it.

Implementing Audit Log / Change History with MVC & Entity Framework

I am building in a Change History / Audit Log to my MVC app which is using the Entity Framework.
So specifically in the edit method public ActionResult Edit(ViewModel vm), we find the object we are trying to update, and then use TryUpdateModel(object) to transpose the values from the form on to the object that we are trying to update.
I want to log a change when any field of that object changes. So basically what I need is a copy of the object before it is edited and then compare it after the TryUpdateModel(object) has done its work. i.e.
[HttpPost]
public ActionResult Edit(ViewModel vm)
{
//Need to take the copy here
var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);
if (ModelState.IsValid)
{
//Form the un edited view model
var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
//Compare with old view model
WriteChanges(uneditedVM, vm);
...
TryUpdateModel(object);
}
...
}
But the problem is when the code retrieves the "unedited vm", this is causing some unexpected changes in the EntityFramework - so that TryUpdateModel(object); throws an UpdateException.
So the question is - in this situation - how do I create a copy of the object outside of EntityFramework to compare for change/audit history, so that it does not affect or change the
EntityFramework at all
edit: Do not want to use triggers. Need to log the username who did it.
edit1: Using EFv4, not too sure how to go about overriding SaveChanges() but it may be an option
This route seems to be going nowhere, for such a simple requirement! I finally got it to override properly, but now I get an exception with that code:
public partial class Entities
{
public override int SaveChanges(SaveOptions options)
{
DetectChanges();
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
//return base.SaveChanges();
return base.SaveChanges(options);
}
}
IF you are using EF 4 you can subscribe to the SavingChanges event.
Since Entities is a partial class you can add additional functionality in a separate file. So create a new file named Entities and there implement the partial method OnContextCreated to hook up the event
public partial class Entities
{
partial void OnContextCreated()
{
SavingChanges += OnSavingChanges;
}
void OnSavingChanges(object sender, EventArgs e)
{
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
}
}
If you are using EF 4.1 you can go through this article to extract changes
See FrameLog, an Entity Framework logging library that I wrote for this purpose. It is open-source, including for commercial use.
I know that you would rather just see a code snippet showing how to do this, but to properly handle all the cases for logging, including relationship changes and many-to-many changes, the code gets quite large. Hopefully the library will be a good fit for your needs, but if not you can freely adapt the code.
FrameLog can log changes to all scalar and navigation properties, and also allows you to specify a subset that you are interested in logging.
There is an article with high rating here at the codeproject: Implementing Audit Trail using Entity Framework . It seems to do what you want. I have started to use this solution in a project. I first wrote triggers in T-SQL in the database but it was too hard to maintain them with changes in the object model happening all the time.

Categories

Resources