I recently upgraded my solution from EF5 to EF6.1.2, and changed my data access layer to use DbContext instead of ObjectContext.
Some of my unit tests are failing, and I don't understand why. Example of old data access code:
public virtual T Insert(T item)
{
if (item == null)
{
throw new ArgumentNullException("item", #"TaskDal.Insert");
}
using (var ctx = ObjectContextManager<StoreDataContext>.GetManager("StoreDataContext"))
{
var task = new Task();
WriteNonKeyData(task, item);
ctx.ObjectContext.Tasks.AddObject(task); // task.taskType null
ctx.ObjectContext.SaveChanges(); // task.TaskType set
return ReadData(task);
}
}
The Task Entity has a navigation property TaskType. As commented above, this gets set after the AddObject line.
My new code looks like so:
public virtual T Insert(T item)
{
if (item == null)
{
throw new ArgumentNullException("item", #"TaskDal.Insert");
}
using (var ctx = DbContextManager<StoreDataContext>.GetManager())
{
var task = new Task();
WriteNonKeyData(task, item);
ctx.DbContext.Tasks.Add(task); // task.TaskType null
ctx.DbContext.SaveChanges(); // task.TaskType still null
return ReadData(task);
}
}
Unlike the old code, task.TaskType is not set, which causes an exception in ReadData. LazyLoading is true in both examples.
I can workaround this by manually reloading the TaskType:
if (task.TaskType == null)
ctx.DbContext.Entry(task).Reference(p => p.TaskType).Load();
but I would prefer a better solution, as I am sure there are hundreds of other places in my code where this will need to be changed and it will be difficult for me to find them all.
Task will not load its navigation properties as these are not implemented to be lazily loaded. Take a look at your class definition, do you see any code in the getter? No.
Now, take a look at the model classes created automatically for your legacy code, is there a non empty getter that supports lazy loading? Yes, there is.
The difference is that with code-first, your model classes have no code that supports lazy loading. Lazy loading is supported only on proxy objects that are created by the context when you retrieve data from the database.
One of simplest workarounds would be to force the EF to create a proxy for you:
using (var ctx = DbContextManager<StoreDataContext>.GetManager())
{
var task = new Task();
WriteNonKeyData(task, item);
ctx.DbContext.Tasks.Add(task); // task.TaskType null
ctx.DbContext.SaveChanges(); // task.TaskType still null
// let ef create a proxy for the very same database object
var ptask = ctx.DbContext.Tasks.First( p => p.ID == task.ID );
// ptask.TaskType is now available as the actual type of
// ptask is not Task but rather a TaskProxy that inherits from Task
// and is created automatically by ef
return ReadData(ptask);
}
Related
I came across this question, and liked how the generic update for one-to-many is implemented.
I tried to mimic it to implement a one-to-one version for myself but could not be totally successful. Following is the result of my struggle -
public async Task<int> UpdateAsync<T>(T entity, params Expression<Func<T, object>>[] navigations) where T : EntityBase
{
var dbEntity = await _DbCtx.FindAsync<T>(entity.Id);
var entry = _DbCtx.Entry(dbEntity);
entry.CurrentValues.SetValues(entry);
foreach (var nav in navigations)
{
string propertyName = nav.GetPropertyAccess().Name;
// Problem #01 //
// if possible, I'd like to avoid this reflection in favor of EF Core MetaData?
var child = (EntityBase)entity.GetType().GetProperty(propertyName).GetValue(entity);
if (child == null)
continue; // if the client-sent model doesn't have child, skip
var referenceEntry = entry.Reference(propertyName);
await referenceEntry.LoadAsync();
var dbChild = (EntityBase)referenceEntry.CurrentValue;
if (dbChild == null)
{
// Problem #02 //
// if the existing entity doesn't have child, the client-sent child will be assigned.
// but I could not figure out how to do this
}
else
{
_DbCtx.Entry(dbChild).CurrentValues.SetValues(child);
}
}
return await _DbCtx.SaveChangesAsync();
}
I have marked the problems as Problem #01 and Problem #02 in code comments above. Any suggestion, solution will be appreciated. Thanks.
Edit :
Alternatively, if you think there is a better, more efficient way of doing the same thing I'm trying to do above, please share your knowledge.
For Problem #01, I couldn't find equivalent of GetCollectionAccessor, but one way I can think of solving it using EF Core metadata, would be calling the Entry method in the disconnected Entity:
var entry = _context.Entry(dbEntity);
entry.CurrentValues.SetValues(entry);
var disconnectedEntry = _context.Entry(entity); // new
foreach (var nav in navigations)
{
string propertyName = nav.GetPropertyAccess().Name;
var navigationChild = disconnectedEntry.Navigation(propertyName).CurrentValue; // new
}
Bear in mind that in every Entry method call EF Core will try to DetectChanges. As this verification is not necessary for this Entity, you can save some performance disabling the AutoDetectChanges as suggested in this issue.
Now for Problem #02, you can just assign the child Entity to the ReferenceEntry CurrentValue, like this:
var referenceEntry = entry.Reference(propertyName);
await referenceEntry.LoadAsync();
var dbChild = (EntityBase)referenceEntry.CurrentValue;
if (dbChild == null)
{
referenceEntry.CurrentValue = navigationChild; // new
}
else
{
_DbCtx.Entry(dbChild).CurrentValues.SetValues(child);
}
I hope it can help you. If some problem arises let me know!
Edit
I also suggest you to read about the TrackGraph method. Depending on how your entities work, maybe everything could be done with this method with a couple of lines.
Within an ASP.NET MVC Application I'm recieving the following error message for one of my controller methods that uses my Entity Framework context.
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
I'm aware that you cannot run queries in parallel, and everything appears to be awaited properly. If I debug the program and step and inspect some of the data returned from EF then it works, probably because this forces the queries to complete.
EDIT If I place a breakpoint at the null check in the controller method and inspect the data of shipmentDetail the exception is NOT thrown.
Here's a snippit of the code:
Controller Method:
[Route("{id:int}/Deliveries")]
public async Task<ActionResult> DeliveryInfo(int id)
{
var shipmentDetail = await db.ShipmentDetails.SingleOrDefaultAsync(s => s.Id == id);
if (shipmentDetail == null)
return HttpNotFound(string.Format("No shipment detail found with id {0}", id));
var model = await DeliveryInfoModel.CreateModel(db, shipmentDetail);
return View("DeliveryInfo", model);
}
CreateModel Method:
public static async Task<DeliveryInfoModel> CreateModel(Context db, ShipmentDetail shipment)
{
DeliveryInfoModel model = new DeliveryInfoModel()
{
ShipmentInfo = shipment
};
//initialize processing dictionary
Dictionary<int, bool> boxesProcessed = new Dictionary<int, bool>();
List<DeliveryBoxStatus> statuses = new List<DeliveryBoxStatus>();
for (int i = 1; i <= shipment.BoxCount; i++ )
{
boxesProcessed.Add(i, false);
}
//work backwards through process
//check for dispositions from this shipment
if(shipment.Dispositions.Count > 0)
{
foreach (var d in shipment.Dispositions)
{
DeliveryBoxStatus status = new DeliveryBoxStatus()
{
BoxNumber = d.BoxNumber,
LastUpdated = d.Date,
Status = d.Type.GetDescription().ToUpper()
};
statuses.Add(status);
boxesProcessed[d.BoxNumber] = true;
}
}
//return if all boxes have been accounted for
if (boxesProcessed.Count(kv => kv.Value) == shipment.BoxCount)
{
model.BoxStatuses = statuses;
return model;
}
//check for deliveries
if(shipment.Job_Detail.Count > 0)
{
foreach (var j in shipment.Job_Detail.SelectMany(d => d.DeliveryInfos))
{
DeliveryBoxStatus status = new DeliveryBoxStatus()
{
BoxNumber = j.BoxNumber,
LastUpdated = j.Job_Detail.To_Client.GetValueOrDefault(),
Status = "DELIVERED"
};
statuses.Add(status);
boxesProcessed[j.BoxNumber] = true;
}
}
//check for items still in processing & where
foreach (int boxNum in boxesProcessed.Where(kv => !kv.Value).Select(kv => kv.Key))
{
//THIS LINE THROWS THE EXCEPTION
var processInfo = await db.Processes.Where(p => p.Jobs__.Equals(shipment.Job.Job__, StringComparison.InvariantCultureIgnoreCase) && p.Shipment == shipment.ShipmentNum && p.Box == boxNum)
.OrderByDescending(p => p.date)
.FirstOrDefaultAsync();
//process returned data
//...
}
model.BoxStatuses = statuses;
return model;
}
I'm not completely sure if it's because of the query made in the controller, or because of the queries made in the loop that aren't completing causing this behavior. Is there something I'm not understanding about when the queries are actually made/returned due to EF's laziness, or how async/await works in this situation? I have a lot of other methods & controllers that make async EF calls and haven't run into this previously.
EDIT
My context is injected into my controller using Ninject as my IoC container. Here's its config inside of NinjectWebCommon's RegisterServices method:
kernel.Bind<Context>().ToSelf().InRequestScope();
Avoid lazy loading when using async with Entity Framework. Instead, either load the data you need first, or use Include()'s to ensure the data you need is loaded with the query.
https://msdn.microsoft.com/en-gb/magazine/dn802603.aspx
Current State of Async Support
... Async
support was added to Entity Framework (in the EntityFramework NuGet
package) in version 6. You do have to be careful to avoid lazy
loading when working asynchronously, though, because lazy loading is
always performed synchronously. ...
(Emphasis mine)
Also:
https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#ThreadSafety
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;
I'm new to using entity as a data layer between MVC and SQL Server, so I apologize up front if what I'm doing is bad practice.
Let me start by sharing the code that is handling the update.
Update Delivery:
public bool One(Delivery toUpdate)
{
using (var dbContext = new FDb())
{
try
{
var deliveryInDb = this.dbTable(dbContext).Single(x => x.DeliveryId == toUpdate.DeliveryId);
dbContext.Entry(deliveryInDb).CurrentValues.SetValues(toUpdate);
//removal first
List<DeliveryDay> currentDays = FEngineCore.DeliveryDay.Get.ForValue((x => x.DeliveryId), toUpdate.DeliveryId);
List<DeliveryTime> currentTimes = FEngineCore.DeliveryTime.Get.ForValue((x => x.DeliveryId), toUpdate.DeliveryId);
//remove delivery days that are not needed
foreach (var curDay in currentDays)
{
if (!toUpdate.DeliveryDays.Select(x => x.DeliveryDayId).Contains(curDay.DeliveryDayId))
{
FEngineCore.DeliveryDay.Delete.One((x => x.DeliveryDayId), curDay.DeliveryDayId);
deliveryInDb.DeliveryDays.Remove(curDay);
}
}
//remove delivery times that are not needed
foreach (var curTime in currentTimes)
{
if (!toUpdate.DeliveryTimes.Select(x => x.DeliveryTimeId).Contains(curTime.DeliveryTimeId))
{
FEngineCore.DeliveryTime.Delete.One((x => x.DeliveryTimeId), curTime.DeliveryTimeId);
deliveryInDb.DeliveryTimes.Remove(curTime);
}
}
foreach (var day in toUpdate.DeliveryDays)
{
if (day.DeliveryDayId == 0)
{
dbContext.DeliveryDays.Add(day);
}
else
{
if (dbContext.DeliveryDays.Local.Any(e => e.DeliveryDayId == day.DeliveryDayId))
{
dbContext.Entry(dbContext.DeliveryDays.Local.First(e => e.DeliveryDayId == day.DeliveryDayId)).CurrentValues.SetValues(day);
dbContext.Entry(dbContext.DeliveryDays.Local.First(e => e.DeliveryDayId == day.DeliveryDayId)).State = EntityState.Modified;
}
else
{
DeliveryDay modDay = new DeliveryDay
{
DayOfWeek = day.DayOfWeek,
DeliveryDayId = day.DeliveryDayId,
DeliveryId = day.DeliveryId,
Interval = day.Interval
};
dbContext.DeliveryDays.Attach(modDay);
dbContext.Entry(modDay).State = EntityState.Modified;
}
deliveryInDb.DeliveryDays.Add(day);
}
}
foreach (var time in toUpdate.DeliveryTimes)
{
if (time.DeliveryTimeId == 0)
{
dbContext.DeliveryTimes.Add(time);
}
else
{
if (dbContext.DeliveryTimes.Local.Any(e => e.DeliveryTimeId == time.DeliveryTimeId))
{
dbContext.Entry(dbContext.DeliveryTimes.Local.First(e => e.DeliveryTimeId == time.DeliveryTimeId)).CurrentValues.SetValues(time);
dbContext.Entry(dbContext.DeliveryTimes.Local.First(e => e.DeliveryTimeId == time.DeliveryTimeId)).State = EntityState.Modified;
}
else
{
DeliveryTime modTime = new DeliveryTime
{
DeliveryId = time.DeliveryId,
DeliveryLocationId = time.DeliveryLocationId,
DeliveryTimeId = time.DeliveryTimeId,
DropoffTime = time.DropoffTime
};
dbContext.DeliveryTimes.Attach(modTime);
dbContext.Entry(modTime).State = EntityState.Modified;
}
deliveryInDb.DeliveryTimes.Add(time);
}
}
dbContext.SaveChanges();
dbContext.Entry(deliveryInDb).State = EntityState.Detached;
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return false;
}
}
}
Let me continue by explaining that the delivery object has 2 children; DeliveryTime and DeliveryDay. The issue that arises happens when I try to remove one deliveryTime and modify nothing else. The end result of running the code normally (not in debug) is that the deliveryTime is in fact not removed. Here's the interesting thing guys, when I debug it and go through the break points, everything works as expected!
Let me continue by posting the code that is running behind the removal method of the deliveryTime (actually all entity objects in my system).
public bool One<V>(Expression<Func<T, V>> property, V value) where V : IComparable
{
using (var dbContext = new FoodsbyDb())
{
try
{
T toDelete;
//get the body as a property that represents the property of the entity object
MemberExpression entityPropertyExpression = property.Body as MemberExpression;
//get the parameter that is representing the entity object
ParameterExpression entityObjectExpression = (ParameterExpression)entityPropertyExpression.Expression;
//represent the value being checked against as an expression constant
Expression valueAsExpression = Expression.Constant(value);
//check the equality of the property and the value
Expression equalsExpression = Expression.Equal(entityPropertyExpression, valueAsExpression);
//create an expression that takes the entity object as a parameter, and checks the equality using the equalsExpression variable
Expression<Func<T, bool>> filterLambda = Expression.Lambda<Func<T, bool>>(equalsExpression, entityObjectExpression);
toDelete = this.dbTable(dbContext)
.SingleOrDefault(filterLambda);
if (toDelete != null)
{
this.dbTable(dbContext)
.Remove(toDelete);
dbContext.SaveChanges();
return true;
}
return false;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return false;
}
}
}
The code above is obviously generic, and it handles all my entity objects. I have tested it in and out and know for sure the problem does not lie in there. I thought it would be helpful to post it so you all can have a full understanding of what's going on.
Here's my best guess as to what's going on:
The reference to the removed deliveryTime still exists when the database context is saved, but when I debug, the system has enough time to remove the context.
Here was one of my attempted solutions:
Remove all references to the children objects immediately after setting currentDays and currentTimes and then proceeding to add them back to deliveryInDb as you enumerate through them.
Because I am new to all of this, if you see some bad practice along with the solution, I wouldn't mind constructive criticism to improve my programming method.
I actually encountered this issue in a project at work. The project is an older MVC4 project using EF 6.1.
In our situation, a simple update attempting to set a related entity property to null was failing to actually set it to null while running the web app normally (in debug mode). When setting a break point on the line of code that sets the property to null the database would be updated as expected, though. So, the update was working when a break point was in place but not working when allowed to run normally.
Using an EF interceptor, we could see that, with the break point in place, the update query was going through as expected.
Now, in our situation the related entity was using the virtual keyword to allow for lazy loading. I think this is the root of the issue. When a break point is present, EF has enough time to both lazily load that related entity and evaluate whatever it needs to evaluate and finally set it to null. When running without a break point, I think EF gets caught up trying to lazily load that entity and therefore fails to think it needs to be updated. To be clear, I was both accessing the related entity property for the first time and setting it null using a one-liner of code.
foo.Bar = null;
I resolved this issue, in our scenario, by accessing that property at least once prior to setting it to null so that EF is forced to load it. With it loaded, setting it to null seems to work as intended now. So again, to be clear, I think the issue is a combo of lazy loading and the one-liner of code both accessing that property for the first time and assigning it to null.
It appears that you're using multiple instances of your DbContext, which are not synchronized.
The solution would be to use a single instance, and pass that instance between your methods.
I have the following issue on update of the entities. Given below is my WCF method. (Update is called by public Save method after determining if it is update or add)
protected bool UpdateSalesMaster(SalesMaster order)
{
using (var context = new MyContext())
{
SalesMaster original = context.SalesMasters.FirstOrDefault(o => o.OrderID == order.OrderID);
if (original != null)
{
context.Entry(original).CurrentValues.SetValues(order);
foreach (SalesDetail detail in order.SalesDetails)
{
if (detail.OrderDetailID == 0)
context.SalesDetails.Add(detail);
else
{
SalesDetails originalDetail = context.SalesDetails.FirstOrDefault(o => o.OrderDetailID == detail.OrderDetailID);
if (originalDetail != null)
context.Entry(originalDetail).CurrentValues.SetValues(detail);
}
}
context.SaveChanges();
return true;
}
else
{
throw new FaultException(string.Format("Invalid Order specified: {0}", order.OrderID));
}
}
}
When I just update the OrderDate in SalesMaster and don't change any in the detail, an update query is fired to the database for detail. I expected to see Update query only for SalesMaster.
Can someone let me know what am I doing wrong here? I don't want to fire update queries to the DB if nothing is changed.
I use the approach of getting the original value from the database to determine if any values are updated using context.Entry(originalDetail).CurrentValues.SetValues(detail);
I also override the SaveChanges to set the LastModified date by checking for IAuditable implementation of the entity. This is when I find that the state of the detail entity is identified as modified. But the only update that happens in the DB is LastModifiedBy which was updated in my save changes. I am not sure how it was set to state of Modified when nothing changed in detail.
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
if (entry.State == EntityState.Added)
entry.Entity.DateCreated = DateTime.Now;
if (entry.State == EntityState.Modified)
{
entry.Property(a => a.CreatedByUser).IsModified = false;
entry.Property(a => a.DateCreated).IsModified = false;
}
entry.Entity.DateModified = DateTime.Now;
}
}
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
throw ex;
}
}
My solution structure is:
Client - Windows forms UI
Entities - POCO as seperate library
WCF - All business logic, add, update, delete of objects.
Data - Entity Framework context with Fluent mapping.
Depending on how you set up you POCOs, EF will default to one of two ways to check changes on an entity.
If ALL your POCOs properties are virtual, EF will use proxy object that inherit your POCO type, with all the properties overridden to track changes.
I assume that in this circumstance, using SetValues on the whole object WILL trigger the dirty flag, that will cause EF to generate an Update query to the database.
If your are not using proxies, your IAuditable implementation would be the primary suspect as brumScouse suggested.