I have an MVC application with the following code in the POST method of the controller. I am doing an EF Add and obviously that is not right. I want it to add the record if it doesn't exist, otherwise Update. How can I do that please?
try
{
AttributeEntities db = new AttributeEntities();
IEnumerable<string> items = viewModel.SelectedAttributes2;
int i = 0;
foreach (var item in items)
{
var temp = item;
// Save it
SelectedHarmonyAttribute attribute = new SelectedHarmonyAttribute();
attribute.CustomLabel = viewModel.ItemCaptionText;
attribute.IsVisible = viewModel.Isselected;
string harmonyAttributeID = item.Substring(1, 1);
// attribute.OrderNumber = Convert.ToInt32(order);
attribute.OrderNumber = i++;
attribute.HarmonyAttribute_ID = Convert.ToInt32(harmonyAttributeID);
db.SelectedHarmonyAttributes.Add(attribute);
db.SaveChanges();
}
}
You would need to check the database for the record you are trying to add/update. If the look-up returns null, that means that it doesn't exist in the database. If it does, you can modify the record that you looked up and call db.SaveChanges() to persist the changes you made to the database.
Edit:
int id = Convert.ToInt32(harmonyAttributeID);
var existingEntry = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == id);
One common way to determine an add or update is by simply looking at an identifier field, and setting the appropriate state.
using System.Data;
SelectedHarmonyAttribute attribute;
using (var db = new YourDbContext())
{
db.Entry(attribute).State = attribute.HarmonyAttribute_ID == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
You could import the System.Data.Entity.Migrations namespace and use the AddOrUpdate extension method:
db.SelectedHarmonyAttributes.AddOrUpdate(attribute);
db.SaveChanges();
EDIT:
I'm assuming that SelectedHarmonyAttributes is of type DbSet
EDIT2:
Only drawback with doing it this way (and it may not be a concern for you), is that your entity isn't responsible for it's own state change. This means that you can update any property of the entity to something invalid, where you might want to internally validate it on the entity itself or maybe do some other processing you always want to occur on update. If these things are a concern for you, you should add a public Update method onto the entity and check for its existence on the database first. e.g:
var attribute = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == harmonyAttributeID);
if (attribute != null)
{
attribute.Update(viewModel.ItemCaptionText, viewModel.Isselected, i++);
}
else
{
attribute = new Attribute(viewModel.ItemCaptionText, viewModel.Isselected);
db.SelectedHarmonyAttributes.Add(attribute);
}
db.SaveChanges();
Your update method might look something like:
public void Update(string customLabel, bool isVisible, int orderNumber)
{
if (!MyValidationMethod())
{
throw new MyCustomException();
}
CustomLabel = customLabel;
IsVisible = isVisible;
OrderNumber = orderNumber;
PerformMyAdditionalProcessingThatIAlwaysWantToHappen();
}
Then make all of the entities' properties public "get" but protected "set" so they can't be updated from outside the entity itself. This might be going off an a bit of a tangent but using the AddOrUpdate method would assume you don't want to control the way an update occurs and protect your domain entity from getting into an invalid state etc. Hope this helps!
Related
I have following method in my mvc controller:
[HttpGet]
public ActionResult UserProfile(String username)
{
var user = db.Users.Find(username);
return View(user);
}
This function returns View with user profile. But result of this is the same, regardless of changes in database.
When I debug it seems like db is not changing at all, while in other controllers everything works just fine.
EDIT:
Place when I make changes
public ActionResult ExecuteRetreive(String username, String ISBN)
{
if (IsValid(username))
{
var resBook = db.Books.Find(ISBN);
var resUser = db.Users.Find(username);
var resRentedBooks = (from rb in db.RentedBooks
join b in db.Books on rb.ISBN equals b.ISBN
where b.ISBN == ISBN
where rb.Login == username
where rb.Returned == null
select rb).FirstOrDefault();
if (resRentedBooks == null)
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "" });
}
resRentedBooks.Returned = DateTime.Now;
resBook.IsRented = false;
resUser.RentedBooks--;
db.SaveChanges();
return RedirectToAction("Success", "FailSuccess");
}
else
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "Niepoprawna nazwa użytkownika" });
}
}
Im new to this so dont laugh at my code :P When I display resUser.RentedBooks--; it is the same every time.
As a follow up to what #JeroenVannevel said in the comments, another problem that you might be having because you're using a static context (and one that I've had to deal with in the past) is that once a specific DbContext has loaded an entity (or a set of entities, in my case) it won't tend to refresh just because some outside changes were made in the database. It loads those entities into Local and just refers to those automatically if you query for it.
The solution, then, is to always put your DbContext calls wrapped up in a using block, since DbContext implements IDisposable.
One word of caution with this approach, since you're using MVC: If you are using lazy loading, and you know that your View will need some information from a child object (or to list the names of a collection of child objects), you will absolutely need to hydrate those child entities before you get out of the using block, or you will find yourself getting exceptions saying that your context has been disposed.
I have the following update function
public void UpdateBatchDefinition(BatchDefinition batchToUpdate)
{
if (batchToUpdate == null)
{
throw new ArgumentNullException("batchToUpdate");
}
BatchDefinition foundDefinition =
this.context.BatchDefinitions.SingleOrDefault(definition => definition.Id == batchToUpdate.Id);
if (foundDefinition != null)
{
if (!string.IsNullOrWhiteSpace(batchToUpdate.Name))
{
foundDefinition.Name = batchToUpdate.Name;
}
if (!string.IsNullOrWhiteSpace(batchToUpdate.Description))
{
foundDefinition.Description = batchToUpdate.Description;
}
if (!string.IsNullOrWhiteSpace(batchToUpdate.LoadType))
{
foundDefinition.LoadType = batchToUpdate.LoadType;
}
if (batchToUpdate.JobId != Guid.Empty)
{
foundDefinition.JobId = batchToUpdate.JobId;
}
foundDefinition.Tables = batchToUpdate.Tables;
this.context.SaveChanges();
}
}
the issue I am having Is when I am trying to update the Tables list. Tables is a List of Table and Table is a Entity of another table
Tables could be added to, removed from or left alone. I need to update that with what ever is being passed in
when I run this right now I get an 'EntityValidationErrors' error, though it wont tell me what the validation issue actually is.
on Inserting I got the same error but was able to fix it using the following
var underlyingContext = this.context as DbContext;
if (underlyingContext != null)
{
foreach (var table in batchDefinition.Tables)
{
// Need to mark the table entity as unchanged or
// else EF will treat it as a new table
underlyingContext.Entry(table).State = EntityState.Unchanged;
}
}
so I tried using that in this update function
var underlyingContext = this.context as DbContext;
if (underlyingContext != null)
{
foreach (var table in foundDefinition.Tables)
{
// Need to mark the table entity as unchanged or
//else EF will treat it as a new table
underlyingContext.Entry(table).State = EntityState.Unchanged;
}
}
foundDefinition.Tables = batchToUpdate.Tables;
and I get the following error instead:
AcceptChanges cannot continue because the object's key values conflict
with another object in the ObjectStateManager. Make sure that the key
values are unique before calling AcceptChanges.
Any thoughts one what I am missing here?
Change end of your update method like this:
foreach (var t in foundDefinition.Tables.ToList())
Context.Tables.Remove(t);
foundDefinition.Tables = batchToUpdate.Tables;
this.context.SaveChanges();
And about your last error, it is said that there are some duplicates in your context. So, EF can't save the context changes into the db (because there are duplicates in the context!)
In fact, I don't know the last error is from add or delete - you didn't mention clearly. So, I don't know the last two code samples are from your add method, or your update method...
However for update, the trick I mentioned here, must solve your problem for update...
var paymentAttempt = _auctionContext.PaymentAttempts.Where(o => o.Id == paymentAttemptId).SingleOrDefault();
if (paymentAttempt != null)
{
paymentAttempt.PaymentAttemptStatusId = (int)PaymentAttemptStatus.Defunct;
paymentAttempt.PaymentAttemptStatus = _auctionContext.PaymentAttemptStatuses.Where(pas => pas.Id == paymentAttempt.PaymentAttemptStatusId).First();
var relevantWinningBidsTotalPrices = _auctionContext.GetWinningBidsTotalPricesForPaymentAttempt(paymentAttemptId).ToArray();
foreach (var winningBid in relevantWinningBidsTotalPrices)
{
winningBid.Locked = false;
_auctionContext.UpdateObject(winningBid);
}
_auctionContext.SaveChanges();
}
In the above code after
_auctionContext.SaveChanges();
is called winningBid is updated as expected but paymentAttempt isn't. Why is this? It is really frustrating. There is no error either. I would expect a failure to occur if there was a problem like EF wasn't tracking the object or something like that, but no such error is happening.
That's because you need to pass the paymentAttempt object to your context, to let it know that it is an object that needs to be updated.
For example, assuming that _auctionContext is an instance of DbContext:
// any changes related to the paymentAttempt object
_auctionContext.Entry(paymentAttempt).State = EntityState.Modified;
foreach (var winningBid in relevantWinningBidsTotalPrices)
{
winningBid.Locked = false;
_auctionContext.UpdateObject(winningBid);
}
_auctionContext.SaveChanges();
Another option is the Attach method:
_auctionContext.Attach(paymentAttempt);
_auctionContext.ObjectStateManager.ChangeObjectState(paymentAttempt, System.Data.EntityState.Modified);
If you don't have Entry try adding:
using System.Data.Entity.Infrastructure;
using System.Data.Entity;
then you may simply use:
_auctionContext.Entry(paymentAttempt).State = EntityState.Modified;
_auctionContext.SaveChanges();
I fell on this question but for a different problem. I discovered that if you call SaveChanges() on an object that hasn't been modified, EF will not update anything. This makes sense, but I needed the DB to be updated so that other users would see that a SaveChanges() had been executed, regardless of whether any fields had changed. To force an update without changing any fields:
Dim entry As DbEntityEntry = entities.Entry(myentity)
entry.State = Entity.EntityState.Modified
I know this is late but there's another explanation worth mentioning. Even though your field name contains ID and may be set to autoincrement, be sure to verify that you declared it in your table the primary key.
I'm having a problem that I thought was easy to resolve.
This is my scenario (Entity framework 4 disconnected entities using self tracking).
Let's say I have 2 entities: Users, Orders.
From an asp.net page a get from the database 1 user and 1 order.
const int userId = 1;
const int orderId = 1;
var userManager = new UserManager();
var orderManager = new OrderManager();
var user = userManager.GetUser(userId);
var order = channelManager.GetChannel(channelId);
user.Orders.Add(order);
Now I need to create a function that updates the user adding the order to it.
I wrote something like:
public bool UpdateUser(User user)
{
context.AttachTo("Users", user);
var stateMgr = context.ObjectStateManager;
var stateEntry = stateMgr.GetObjectStateEntry(user);
for (int i = 0; i < stateEntry.CurrentValues.FieldCount; i++)
{
bool isKey = false;
string name = stateEntry.CurrentValues.GetName(i);
foreach (var keyPair in stateEntry.EntityKey.EntityKeyValues)
{
if (string.Compare(name, keyPair.Key, true) == 0)
{
isKey = true;
break;
}
}
if (!isKey)
{
stateEntry.SetModifiedProperty(name);
}
}
context.ApplyCurrentValues("Users", user);
return context.SaveChanges() > 0;
}
I don't have any error on this function and debugging everything seems to be ok, but when I check on the database the entity is not updated as expected.
I thought update a disconnected entity was something simple but apparently is not.
Can someone explaing me the logic between the update the entire graph of disconnected object with EF4? Please if you can I need to undestand the logic and not have a collection of links to look at. I already spent some time looking on internet but I'm finding so many approches that I'm not sure which one is correct.
Thanks
I don't see anything related to Self tracking entities in your code. Anyway STEs with ASP.NET don't work very well.
What is your code supposed to do? It looks like you want to do this:
public bool UpdateUser(User user)
{
context.AttachTo("Users", user);
context.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);
return context.SaveChanges() > 0;
}
But it will not save relations. I just answered some related question about working with detached graphs.
With Self Tracking Entities you have to use the ApplyChanges() method to sync the change on a context instead (and not attach).
The applychanges will go to the graph to update the linked object/collection.
Public Function UpdateEntity(entity As Entity) As Entity
Using dbContext As New EntityContext()
dbContext.EntitySet.ApplyChanges(entity)
dbContext.SaveChanges()
dbContext.Refresh(Objects.RefreshMode.StoreWins, entity)
End Using
Return entity
End Function
The refresh is optionnal, it is here only to push back the last value of the database. By the way the refresh doesn't update the linked object.
I am using Entity Framework & LINQ to retrieve data. I am having a problem with the following:
var customer= db.customers.where(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
The fields are changing in the database before I call:
db.SaveChanges();
How do I avoid this?
As others have said, I believe that you are using your context in another place as well and that other location is calling savechanges and updating everything. Try doing what #Evan suggested with a using statment to make sure you have a fresh context.
AsNoTracking will not ensure that you get a entity that is not cached in the database, its purpose is to not put the objects inside the context. If you use AsNoTracking and then change the entities returned in the query you will need to attach them as modified to the context before calling savechanges or else they won't be updated.
var customer= db.customers.AsNoTracking().Single(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
ctx.customers.Attach(customer);
ctx.ObjectStateManager.ChangeObjectState(customer, System.Data.EntityState.Modified);
I would have just commented on the other posts but don't have enough rep yet.
Check whether you are passing the db object to some other method, and SaveChanges() is called there?
Or check whether you have a catch block of an exception and you might be using SaveChanges() in the catch block to log error message?
(These are common programming mistakes)
The fields are changing in the database before I call
If you mean changing as in changing outside of application, changes in SQL Management Studio for example. Entity Framework cannot detect those changes, so as a result you might get stale objects that was cached by Entity Framework. To prevent receiving cached object and get the up-to-date values from database, use AsNoTracking.
Try putting AsNoTracking():
var customer= db.customers.AsNoTracking().where(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
db.SaveChanges();
Or if your problem is to detect concurrent updates(unfortunate terminology, it doesn't apply to UPDATE only) to same row, use rowversion(aka timestamp) field type; then on your .NET code add Timestamp attribute on the property. Example: http://www.ienablemuch.com/2011/07/entity-framework-concurrency-checking.html
public class Song
{
[Key]
public int SongId { get; set; }
public string SongName { get; set; }
public string AlbumName { get; set; }
[Timestamp]
public virtual byte[] Version { get; set; }
}
UPDATE (after your comment):
If you really has no intent to persist your object changes to database. Try detaching the object.
Try this:
var customer= db.customers.where(c=>c.id==1);
db.Entry(customer).State = System.Data.EntityState.Detached; // add this
customer.name=santhosh;
customer.city=hyd;
db.SaveChanges();
That won't save your changes on name and city to database.
If you want something more robust(the above will fail an exception if the object was not yet attached), create a helper:
private static void Evict(DbContext ctx, Type t,
string primaryKeyName, object id)
{
var cachedEnt =
ctx.ChangeTracker.Entries().Where(x =>
ObjectContext.GetObjectType(x.Entity.GetType()) == t)
.SingleOrDefault(x =>
{
Type entType = x.Entity.GetType();
object value = entType.InvokeMember(primaryKeyName,
System.Reflection.BindingFlags.GetProperty,
null, x.Entity, new object[] { });
return value.Equals(id);
});
if (cachedEnt != null)
ctx.Entry(cachedEnt.Entity).State = EntityState.Detached;
}
To use: Evict(yourDbContextHere, typeof(Product), "ProductId", 1);
http://www.ienablemuch.com/2011/08/entity-frameworks-nhibernate.html
Can you give a little more of the surrounding code? Might be a little difficult without seeing how you are constructing your context.
This is how I typically handle updates (I hope it might give some insight):
using (var ctx = new myModel.myEntities())
{
int pollID = 1;
var poll = (from p in ctx.Polls
where p.PollID == pollID
select p).FirstOrDefault();
poll.Question = txtPoll.Text.Trim();
ctx.SaveChanges();
}
Jack Woodward, yours did not work for me.
I had to change it up a little for SQL Compact.
var customer= db.customers.AsNoTracking().Single(c=>c.id==1);
db.customers.Attach(customer);
customer.name=santhosh;
customer.city=hyd;
db.ObjectStateManager.ChangeObjectState(customer, System.Data.EntityState.Modified);
db.SaveChanges();
db.Dispose();
This worked alot better.