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.
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.
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);
}
I'm experimenting with some queries to find out the best way to get performance gains.
I know that using IQueryable is preferable to performing Linq to Sql or Linq to Entity database queries and that IEnumerable is best used for linq to Objects, Linq to xml, and in memory processing.
I have a linq query as follows on my WCF service. When I try and modify the Controller method that calls this, I get the following design time compile error:
Cannot implicitly convert type 'YeagerTechModel.DropDownLists.ProjectDescription[]' to 'System.Linq.IQueryable'
Note that the ProjectDescription object is defined as follows:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
namespace YeagerTechModel.DropDownLists
{
[DataContract]
[Serializable]
public partial class ProjectDescription
{
[DataMember]
public Int16 ProjectID { get; set; }
[DataMember]
public String Description { get; set; }
}
}
Here is the DB method call:
public IQueryable<ProjectDescription> GetProjectDropDownList()
{
try
{
using (YeagerTechEntities DbContext = new YeagerTechEntities())
{
DbContext.Configuration.ProxyCreationEnabled = false;
DbContext.Database.Connection.Open();
IQueryable<ProjectDescription> project = DbContext.Projects.Where(w => w.Notes != null).Select(s =>
new ProjectDescription()
{
ProjectID = s.ProjectID,
Description = s.Description
}
);
return project;
}
}
catch (Exception ex)
{
throw ex;
}
}
Here is the code in the Controller method:
IQueryable<ProjectDescription> projectDdl = db.GetProjectDropDownList();
Now, prior to this experimentation after reading up on the performance gains of IQueryable, etc, the original method to get the data from the database was as follows:
public List<ProjectDescription> GetProjectDropDownList()
{
try
{
using (YeagerTechEntities DbContext = new YeagerTechEntities())
{
DbContext.Configuration.ProxyCreationEnabled = false;
DbContext.Database.Connection.Open();
var project = DbContext.Projects.Where(w => w.Notes != null).Select(s =>
new ProjectDescription()
{
ProjectID = s.ProjectID,
Description = s.Description
}
);
List<ProjectDescription> myProjects = new List<ProjectDescription>();
myProjects = project.ToList();
return myProjects;
}
}
catch (Exception ex)
{
throw ex;
}
}
The code in the Controller was as follows:
IEnumerable<ProjectDescription> projectDdl = db.GetProjectDropDownList();
First question is:
Many queries use the var keyword to infer the type coming back. Which one to use when calling the DB to retrieve records? The "var" syntax or the "IQuerable" syntax"?
The second thing I noticed is that on the Controller side, for a collection, it always expects a List object which is easily converted to IEnumerable.
So, based on this premise, I gather that my optimum solution would be as follows:
For the DB method call:
public List<ProjectDescription> GetProjectDropDownList()
{
try
{
using (YeagerTechEntities DbContext = new YeagerTechEntities())
{
DbContext.Configuration.ProxyCreationEnabled = false;
DbContext.Database.Connection.Open();
IQueryable<ProjectDescription> project = DbContext.Projects.Where(w => w.Notes != null).Select(s =>
new ProjectDescription()
{
ProjectID = s.ProjectID,
Description = s.Description
}
);
List<ProjectDescription> myProjects = new List<ProjectDescription>();
myProjects = project.ToList();
return myProjects;
}
}
catch (Exception ex)
{
throw ex;
}
}
For the code snippet in the Controller, it should be as follows and everything works fine:
IEnumerable<ProjectDescription> projectDdl = db.GetProjectDropDownList();
So, if IQueryable gives better performance (specifically on filtering and supports lazy loading), why not use the last DB method instead of the "var" keyword?
Can somebody help explain what should be the optimum scenario?
Whether you use var or take the time to type out the variable's type isn't really an issue. Your second and third examples both compile into exactly the same code.
Your first implementation, however, is much better than the other two. Your first method return a query. The other two return the results of that query.
So the first implementation allows the caller to apply further filters/mappings/manipulations of that query and have them be reflected in the database query that is called, rather than on the results in memory. It also means that you're deferring actually executing that query until later on when you need it, rather than right now.
That implementation does have a flaw though; you're deferring execution but also disposing of the underlying context before the query is executed. You'll need to scope your context at a "higher" level to ensure that it has not yet been disposed of until after the query has been executed.
As for the error, you haven't shown enough information to see where the problem lies, but you should make an effort to fix it without just doing all of your data manipulation in your application instead of in your database.
Side note: there's no point in catching an exception just to rethrow it. You're doing nothing productive but clearing out the stack trace. Just don't catch the exception in the first place if you have nothing to do with it.
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.
I have a controller that updates values in a database using Entity Framework. Unfortunately, when I run my application it doesn't seem to work at all. When I put breakpoints in and step through a specific part of the code, it works perfectly.
Here's my controller code:
public ActionResult ManageGame(int id, FormCollection collection, string[] selectedPlayers)
{
var gameToUpdate = db.Games
.Include("Teams")
.Where(g => g.ID == id)
.Single();
if (TryUpdateModel(gameToUpdate, "", null, new string[] { "Players" }))
{
try
{
List<Player> team1Players = generateRandomTeam();
List<Player> team2Players = generateRandomTeam();
If I put a breakpoint here and step through the rest of the code it's fine, otherwise nothing gets saved.
foreach (var team in gameToUpdate.Teams)
{
if (!team1added)
{
team.Players = team1Players;
team1added = true;
}
else
{
team.Players = team2Players;
}
}
db.Entry(gameToUpdate).State = EntityState.Modified;
db.SaveChanges();
}
catch (DataException)
{
ModelState.AddModelError("", "Unable to save changes.");
}
}
try
{
return RedirectToAction("Index");
}
catch
{
return View();
}
}
I have a feeling it's the way I'm assigning the new teams to the existing context, but from all the tutorials I've read, this is the way they do it, at least for string values. Does anybody know why I'm getting this bizarre behavior?
*UPDATE* SOLVED
I solved my problem. My hunch was right, I just needed to add team.Players.Clear() before assigning the new group of players to the existing team.
foreach (var team in gameToUpdate.Teams)
{
if (!team1added)
{
team.Players.Clear()
team.Players = team1Players;
team1added = true;
}
else
{
team.Players.Clear()
team.Players = team2Players;
}
}
When I didn't have that, I got a primary key violation exception. Unfortunately I didn't see this exception, because my code was swallowing this as pointed out by DarK. So, after adding the Clear() method everything worked like a charm.
Looks like other people have had the same problem like yours. Have alook at these links: C# code only gives expected results on step through?, Code runs correctly only when stepping through it with debugger?
So, if you are instantiating the Random class more than once, you will get some weird results.
EDIT:
From your code, it looks like you're consuming the exception. Can you possibly comment out the try-catch and run it without debugging and see if it throws any exceptions?