Entity Framework 5 - Immediately refresh DbContext after saving changes - entity-framework

I have an MVC application that uses Entity Framework 5. In few places I have a code that creates or updates the entities and then have to perform some kind of operations on the updated data. Some of those operations require accessing navigation properties and I can't get them to refresh.
Here's the example (simplified code that I have)
Models
class User : Model
{
public Guid Id { get; set; }
public string Name { get; set; }
}
class Car : Model
{
public Guid Id { get; set; }
public Guid DriverId { get; set; }
public virtual User Driver { get; set; }
[NotMapped]
public string DriverName
{
get { return this.Driver.Name; }
}
}
Controller
public CarController
{
public Create()
{
return this.View();
}
[HttpPost]
public Create(Car car)
{
if (this.ModelState.IsValid)
{
this.Context.Cars.Create(booking);
this.Context.SaveChanges();
// here I need to access some of the resolved nav properties
var test = booking.DriverName;
}
// error handling (I'm removing it in the example as it's not important)
}
}
The example above is for the Create method but I also have the same problem with Update method which is very similar it just takes the object from the context in GET action and stores it using Update method in POST action.
public virtual void Create(TObject obj)
{
return this.DbSet.Add(obj);
}
public virtual void Update(TObject obj)
{
var currentEntry = this.DbSet.Find(obj.Id);
this.Context.Entry(currentEntry).CurrentValues.SetValues(obj);
currentEntry.LastModifiedDate = DateTime.Now;
}
Now I've tried several different approaches that I googled or found on stack but nothing seems to be working for me.
In my latest attempt I've tried forcing a reload after calling SaveChanges method and requerying the data from the database. Here's what I've done.
I've ovewrite the SaveChanges method to refresh object context immediately after save
public int SaveChanges()
{
var rowsNumber = this.Context.SaveChanges();
var objectContext = ((IObjectContextAdapter)this.Context).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, this.Context.Bookings);
return rowsNumber;
}
I've tried getting the updated object data by adding this line of code immediately after SaveChanges call in my HTTP Create and Update actions:
car = this.Context.Cars.Find(car.Id);
Unfortunately the navigation property is still null. How can I properly refresh the DbContext immediately after modifying the data?
EDIT
I forgot to originally mention that I know a workaround but it's ugly and I don't like it. Whenever I use navigation property I can check if it's null and if it is I can manually create new DbContext and update the data. But I'd really like to avoid hacks like this.
class Car : Model
{
[NotMapped]
public string DriverName
{
get
{
if (this.Driver == null)
{
using (var context = new DbContext())
{
this.Driver = this.context.Users.Find(this.DriverId);
}
}
return this.Driver.Name;
}
}
}

The problem is probably due to the fact that the item you are adding to the context is not a proxy with all of the necessary components for lazy loading. Even after calling SaveChanges() the item will not be converted into a proxied instance.
I suggest you try using the DbSet.Create() method and copy across all the values from the entity that you receive over the wire:
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
this.Context.Entry(newEntry).CurrentValues.SetValues(obj);
return newEntry;
}
UPDATE
If SetValues() is giving an issue then I suggest you try automapper to transfer the data from the passed in entity to the created proxy before Adding the new proxy instance to the DbSet. Something like this:
private bool mapCreated = false;
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
if (!mapCreated)
{
Mapper.CreateMap(obj.GetType(), newEntry.GetType());
mapCreated = true;
}
newEntry = Mapper.Map(obj, newEntry);
this.DbSet.Add(newEntry;
return newEntry;
}

I use next workaround: detach entity and load again
public T Reload<T>(T entity) where T : class, IEntityId
{
((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
return _dbContext.Set<T>().FirstOrDefault(x => x.Id == entity.Id);
}

Related

Related data not being added for existing parent entity

Im trying to save a rating against a place, I have the code below, but it doesnt seems to save rating (to the ratings table) for an existing entity
place.Ratings.Add(rating);
_placeRepository.AddPlaceIfItDoesntExist(place);
_placeRepository.Save();
This is the repository method
public void AddPlaceIfItDoesntExist(Place place)
{
var placeItem = context.Places.FirstOrDefault(x => x.GooglePlaceId == place.GooglePlaceId);
if(placeItem==null)
{
context.Places.Add(place);
}
else
{
context.Entry(placeItem).State = EntityState.Modified;
}
}
and this is the poco
public class Place
{
public Place()
{
Ratings = new List<Rating>();
}
public int Id { get; set; }
public string Name { get; set; }
public string GooglePlaceId { get; set; }
}
I think the crux of the problem is because i need to check if the place exists based on googleplaceid(a string) rather than the id (both are unique per place btw)
Here
context.Entry(placeItem).State = EntityState.Modified;
you just mark the existing placeItem object as modified. But it's a different instance than the passed place object, hence contains the orginal values.
Instead, replace that line with:
context.Entry(placeItem).CurrentValues.SetValues(place);
Alternatively, you can use the DbSetMigrationsExtensions.AddOrUpdate method overload that allows you to pass a custom identification expression:
using System.Data.Entity.Migrations;
public void AddPlaceIfItDoesntExist(Place place)
{
context.Places.AddOrUpdate(p => p.GooglePlaceId, place);
}

Adding New Objects with Entity Framework Repository Pattern

I am using Entity Framework and implementing the Repository pattern. Every example that I've been of adding new objects is something like this:
class MyRepository
{
public MyContext Context { get; set; }
public Add(MyObject myObject)
{
this.Context.MyObjects.Add(myObject);
}
public Save()
{
this.Context.SaveChanges();
}
}
// A window which lets the user add items to the repository
class MyWindow
{
private MyRepository Repository { get; set; }
private void DoSomething()
{
List<MyClass> myObjects = this.Repository.GetMyObjects();
// When I create a new object, I have to add the new object to the myObjects list and separately to the repository
MyClass newObject = new MyClass();
myObjects.Add(newObject);
this.Repository.Add(newObject);
// Do stuff to the objects in "myObjects"
this.Repository.Save();
}
}
What I want to be able to do is add new objects to the myObjects list (without having to add them to the repository on a separate line), and then just call something like this.Repository.Save(myObjects) when I'm ready to save them. Having to explicitly add every new object to the repository seems to break up the separation-of-concerns model. Is there a recommended way to do this, or is my reasoning flawed?
EDIT: DDiVita - I'm not sure what you mean by "attaching the entities to the context". This is what I'm currently doing in my Repository class:
public List<MyObject> GetMyObjects()
{
return this.Context.MyObjects.ToList();
}
Then in my Context class:
class MyContext : Context
{
public DbSet<MyObject> MyObjects { get; set; }
}
You can use the AddOrUpdate extension (the link is for Version 6 of EF) method on the DbSet. With this you can specify an identifier that EF will recognize as a unique value to either update or add the entity.
Let's assume your entity MyObject looks like this and the Id is always unique in your database:
public class MyObject
{
public int Id { get; set; }
public string Name { get; set; }
}
public Save(List<MyObject> myObjects)
{
this.Context.MyObjects.AddOrUpdate(m => m.Id,myObjects.ToArray());
this.Context.SaveChanges();
}
What you could do is use AddRange
public Save(List<MyObject> myObjects)
{
this.Context.MyObjects.AddRange(myObjects);
this.Context.SaveChanges();
}
And then your code could look like this
private void DoSomething()
{
List<MyObject> myObjects = this.Repository.GetMyObjects();
MyObject newObject = new MyObject();
myObjects.Add(newObject);
// Do stuff to the objects in "myObjects"
this.Repository.Save(myObjects);
}

DbSet<>.Local ObservableCollection not saving to EF context database

I have a WPF MVVM application with a DataGrid bound to an ObservableCollection returned by DbSet<>.Local. The grid displays content from the database correctly, and changes to the grid change the ObservableCollection, but no changes are saved back to the database.
Context.cs
public class AppContext: DbContext
{
public AppContext() : base("name=DefaultConnection")
{
}
public DbSet<Field> Fields { get; set; }
}
ViewModel.cs
public class EditorViewModel : NotificationObject
{
private ObservableCollection<MyEntity> _myEntities;
private string _message;
public EditorViewModel()
{
var db = new AppContext();
db.MyEntities.Load();
this.MyEntities = db.MyEntities.Local;
}
public ObservableCollection<MyEntity> MyEntities
{
get
{
return _myEntities;
}
set
{
if (_myEntities != value)
{
_myEntities = value;
RaisePropertyChanged("MyEntities");
}
}
}
}
I had thought that changes to the ObservableCollection would automatically write back to the database? Or does SaveChanges need to be called somewhere?
So the answer to this is that using DbSet<>.Local keeps the ObservableCollection in sync with the context, you then just need to call SaveChanges on the context to write back to the database.
http://msdn.microsoft.com/en-gb/data/jj592872.aspx

Entity Framework 4.1 insert error

i have written a generic repository for my base windows which have a problem with.
lets be more specific, there is a little poco class called Unit as following:
public class Unit : BaseEntity
{
public string Name { get; set; }
private ICollection<Good> _goods;
public virtual ICollection<Good> Goods
{
get
{
if(_goods==null)
{
return new List<Good>();
}
return _goods;
}
set { _goods = value; }
}
}
which is inherited from a base entity class as :
public class BaseEntity
{
public int Id { get; set; }
public override string ToString()
{
return Id.ToString();
}
}
and this is my Add section of generic repository class:
public void Add(TEntity entity)
{
if (entity == null) return;
if (Context.Entry(entity).State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Set<TEntity>().Add(entity);
Context.SaveChanges();
}
before add a new record, max id is fetched from db and placed in IdTextBox and them add method of base form is called which calls aforementioned Add method of base repository. here is the problem, i get this error, "The property 'Id' is part of the object's key information and cannot be modified."
there is also a mapper class that maps every property to its corresponding control which does its job fine.
What is my problem?
Thanks in advance.
i figured out that this problem is occured because of auto detect changes enability which was true.

ASP.NET2 ViewModel not Updating

I'm using the NerdDinner MVC1 code. Created a viewmodel called DinnerFormViewModel:
public class DinnerFormViewModel
{
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
I pass to my edit view fine which uses strongly typed helpers in MVC2. Passing back however:
public ActionResult Edit(int id, FormCollection collection)
{
Dinner dinnerToUpdate = dr.GetDinner(id);
try
{
UpdateModel(dinnerToUpdate, "Dinner"); // using a helper becuase of strongly typed helpers to tell it what to update
// updates the properites of the dinnerToUpdate object using incoming form parameters collection.
// UpdateModel automatically populates ModelState when it encounters errors
// works by when trying to assign BOGUS to a datetime
// dinnerToUpdate.Country = Request.Form["Countries"];
dr.Save(); // dinner validation may fail here too due to hook into LINQ to SQL via Dinner.OnValidate() partial method.
return RedirectToAction("Details", new { id = dinnerToUpdate.DinnerID });
}
This works, however my Country doesn't get updated, becuase I'm only giving the hint to UpdateModel to update the Dinner.
Question: How to get the country saving?
Thanks.