I have a little problem and I need your help with it. I'm using Entity Framework for database handling and I want to update a dataset in this database.
I have an EntityObject with all the changes and want to be able to update this Object with existing Object.
I'm using the following code to update the data:
IQueryable<Competitors> getCompetitor = DatabaseObject.Competitors.Where(SelectOnly => SelectOnly.competitorID == competitorObject.competitorID);
Competitors competitor = getCompetitor.First();
competitor = competitorObject;
DatabaseObject.SaveChanges();
But this deosn't work. How can I update the date in database?
Assuming, that your competitorObject is of Competitors type (competitor = competitorObject), you have to attach it to your context, mark it as modified, and then save changes:
DatabaseObject.Competitors.Attach(competitorObject);
DatabaseObject.Entry(competitorObject).State = EntityState.Modified;
DatabaseObject.SaveChanges();
There's no need to retrieve source object in your case, but without attaching, the context knows nothing about your updated object.
The piece of code, which is marking an object as modified, can be a little different, if you're using ObjectContext API instead of DbContext API:
DatabaseObject.ObjectStateManager.GetObjectStateEntry(competitorObject).SetModified();
The only change you need to make to your code to get it to work is update at least one property on the fetched entity. You are updating the reference, not the property values so like this:
IQueryable<Competitors> getCompetitor = DatabaseObject.Competitors.Where(SelectOnly => SelectOnly.competitorID == competitorObject.competitorID);
Competitors competitor = getCompetitor.First();
competitor.Name = competitorObject.Name;
competitor.Contact = competitorObject.Contact;
DatabaseObject.SaveChanges();
Or as Dennis has said you can attach the CompetitorObject to the context and mark it as modified. Doing it that way will override all the properties of the existing Competitors record with the values of CompetitorObject.
Related
Let's assume I have the following situation, the update method in my service accepts a model (the one that is going to be updated) as an input parameter. The model can be unattached (in which case attach method is called before submitting changes) or attached (in which case we just submit changes). Edit actions just call this update method in my service. Now let's assume I cannot change the code in those actions (the code that produces the model to be updated). Can I still somehow prevent certain columns from updating from within the update method. Note that I might want to set those columns using linq to SQL, but only during insert method.
I'm quite sure I'm trying something unconventional here, but it might help me write some easy to reuse code. If it cannot be done, then I'll solve it differently, but it never hurts to try something new.
The Attach method does provide an override to accept both a modified and original entity.
Attach Modified Entity on Data Context
When using this the internal change tracker will figure out which columns have been updated and will only update those ones on the datasource which have changed, rather than updating all columns.
Alternatively if you want more explicit control over which properties are updated, you can reattach your entity as unmodified in its original state:
Attach Modified/Unmodified Entity on Data Context
This will hook up the internal change tracker to the PropertyChanging events on the entity so it can be tracked. You would then simply change the values of the properties on that entity in the Update method on your Service.
void Update(MyModel model)
{
using (MyContext ctx = new MyContext())
{
ctx.DeferredLoadingEnabled = false;
ctx.MyEntities.Attach(model.OriginalEntity);
model.OriginalEntity.Value = model.ModifiedEntity.Value;
ctx.SubmitChanges();
}
}
The pitfall of these approaches means you must maintain both the original and modified entities in your model, but could be set when your entities are loaded - a simple shallow copy of the object should do the trick by deriving from ICloneable in a partial class for each entity.
Say changes have been made to a detached entity in EF 4. If we want to save theses changes when we re-attached the entity, is it possible to do this with ApplyCurrentValues without querying the DB to get the original entity? I don't think so, but I'd like somebody to confirm that.
using (var ctx = new BAEntities())
{
var firstCust = (from c in ctx.Contacts select c).First();
Console.WriteLine(firstCust.FirstName);
ctx.Contacts.Detach(firstCust);
firstCust.FirstName = "Modified Value";
ctx.Contacts.Attach(firstCust);
ctx.ApplyCurrentValues("Contacts", firstCust);//Does not work
//ctx.ObjectStateManager.ChangeObjectState(firstCust, EntityState.Modified); //Works with that line
ctx.SaveChanges( );
}
Thank you
I can confirm your guess. It doesn't work this way.
When you call Attach with an entity as parameter EF adds the entity to the context in state Unchanged. Basically you are telling EF with Attach that all property values the entity has at that time represent the current values in the database.
ApplyCurrentValues is kind of an "automapper" that just copies the property values of the object you pass into ApplyCurrentValues to the attached entity that has the same key. This copy happens based on the property name.
If a property value of the attached entity is different to the property value of the object you pass into ApplyCurrentValues EF marks the property as Modified. If not the state stays Unchanged. Obviously with your procedure all property states will stay unchanged and nothing gets written to the database.
In theory you could do crazy stuff to make it work like:
firstCust.FirstName = "Modified Value";
var dummyCust = new Contact { FirstName = "UnlikelyNameThatWillNeverOccur" };
ctx.Contacts.Attach(dummyCust);
ctx.ApplyCurrentValues("Contacts", firstCust);
Here the FirstName property would be marked as Modified. But you had to do this for every property and the result will be the same as setting the state of the whole entity to Modified as you did in the commented code line.
You can by the way set a single property to Modified:
ctx.Contacts.Attach(firstCust);
ctx.ObjectStateManager.GetObjectStateEntry(firstCust)
.SetModifiedProperty("FirstName");
This will send an UPDATE statement to the database that only sets the FirstName column value (while setting the state of the whole entity to Modified will create an UPDATE statement that sets all column values to the current property values).
I'm struggling with Entity Framework code first and merging.
I have an MVC controller with a generic repository. A view model gets posted up and I convert that into the type that EF knows about
var converted = AutoMapper.Mapper.Map<RoutineViewModel, Routine>(result);
_routineRepository.Update(converted);
In the repository I have:
/*
Routines.Attach(item);
ChangeTracker.Entries<Routine>().Single(x => x.Entity.Id == item.Id).State = EntityState.Modified;*/
var match = Routines.Single(x => x.Id == item.Id);
var entity = Entry(match);
entity.CurrentValues.SetValues(item);
I commented out the first bit because it was throwing an error about already tracking the entity even though a check like this:
if (ChangeTracker.Entries<Routine>().Count(x => x.Entity.Id == item.Id) != 0)
returned false
The problem I'm having is that the Routine object has an ICollection property of Steps. When I set the values of the tracked entity to match that of the poco the ICollection changes aren't propagated down. Looking around this site there looks to be a few nasty looking recursive calls. Is this really how it works or am I missing something?
Is there any easy way to say, here is the source object (untracked), copy everything about it into the tracked object?
Just to be clear I don't think that getting the object first and updating the properties on that should be done outside of the repository. That seems to not only force you to pass your data models across domain boundaries but seems like instead of an equivalent SQL like statement being (update x,y where id = 1), to (insert into temp table where id = 1, for reach row in temp table, update x..... now for each row in table update table x = tempx where id = 1)
Edit --
So the problem is with the setValues not being a recursive call. The routine object has 2 simple properties (id and name) and one complex (ICollection ). If the item coming in has the name changed and some steps changed, setValues picks up the name change but doesn't apply to the children. Is there some other way to do this? It seems a little creaky to me that I have to hand roll this functionality
From what i can tell you are creating your entity, populating properties and then attaching it to the DB. This is kinda the wrong way round with EF.
If you want to attach an object which is already in the DB but isnt being tracked, you can use attach but only changes made after the attach call are recorded to be committed to the DB. If you want to use attach make sure you make your changes after calling that method.
In addition EF only allows you to attach an object which is not currently in the object graph. So if you try to attach the same object twice (or one with the same key) you will be given an error such as the one you are seeing.
I'm using the Entity Framework, and it's entities are used are Database representation AND business object.
So it means that some entities that are manipulated should always stay detached from the context.
I managed to read and write data from the database but I have a small problem when updating:
I have a table "Stock" which is linked to one table "Warehouse".
The current process is this one (simplified, but the spirit remains, there are more fields):
a new object Stock is created and its fields are filled with some values (date...)
the current Warehouse (object pulled for the entire request from the database) is associated to the Stock object
the object is sent to the DAL method which work is to save it.
The DAL method checks if the Stock item already exist for the day (same date, depot and same type) in the database
If it exist, the method updates the volume from the pulled object and save the changes.
Else, the new Stock object is inserted.
The problem here is that when I create the new Stock object and associate it to the Warehouse, the object EntityState is automatically set to "Added". So when I perform a SaveChanges() and the Stock already exist, the line is updated AND a new Stock line is added...
What I would want is to keep the new Stock object Detached until I attach it myself. I don't want that it happens automatically.
The only solution I found is to Detach the new object from the context before saving if the object already exist.
I could also Detach() the Warehouse object but that's not a satisfying solution I think as in the real case there are more items to associate and I'm not sure that's a good idea to play with Attach() and Detach() on them.
In this case, until I "Add" it to the context myself, the object is only a "Transport" object and I'd like it to stay out of the context.
Any idea on how I could keep the Stock object detached ?
Code (it may be a little incorrect, I wrote it by memory) :
Stock stk = new Stock();
stk.Date = DateTime.Now;
stk.Volume = 100; //so far stk is "Detached" and that's cool.
stk.Warehouse = CurrentWarehouse; //stk become "Added" and that's less cool.
DAL.Stock.Instance.Save(stk);
In Save():
var existing = (from s in Context.CurrentContext.Stock
where s.Warehouse.WarehouseId == stk.Warehouse.WarehouseId && s.Date == stk.Date && s.Type == 2
select s).FirstOfDefault();
if(existing != null)
{
existing.Volume = stk.Volume;
Context.CurrentContext.Detach(stk); //I find it a stupid workaround !!!!!!
}
else
{
Context.CurrentContext.AddToStock(stk); //what I would want to do.
}
Context.CurrentContext.SaveChanges()
You probably just want to set the MergeOption to an appropriate value. NoTracking would keep everything in a detached state, and allow you to perform your manual work. There are probably other ways to do this, but I'm doing something similar by setting MergeOption to detached.
http://msdn.microsoft.com/en-us/library/system.data.services.client.dataservicecontext.mergeoption.aspx
I have the following scenario:
Entities are loaded from the database.
One of them is presented to the user in a Form (a WPF UserControl) where the user can edit properties of that entity.
The user can decide to apply the changes to the entity or to cancel the editing.
How would I implement something like this with the EntityFramework?
My problem is that, when I bind the UI directly to the Properties of the Entity, every change is instantanously applied to the entity. I want to delay that to the moment where the user presses OK and the entity is validated successfully.
I thought about loading the Entities with NoTracking and calling ApplyPropertyChanges after the detached entity has been validated, but I'm not entirely sure about the correct way to do that. The docu of the EntityFramework at MSDN is very sparse.
Another way I could think of is to Refresh the entity with StoreWins, but I don't like resetting the changes at Cancel instead of applying changes at Ok.
Has anyone a good tutorial or sample?
One options is what you said do a no-tracking query.
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First(c => c.ID == 232);
Then the customer can modify 'customer' as required in memory, and nothing is actually happening in the context.
Now when you want actually make the change you can do this:
// get the value from the database
var original = ctx.Customers.First(c => c.ID == customer.ID);
// copy values from the changed entity onto the original.
ctx.ApplyPropertyChanges(customer); .
ctx.SaveChanges();
Now if you are uncomfortable with the query either for performance or concurrency reasons, you could add a new extension method AttachAsModified(...) to ObjectContext.
that looks something like this:
public static void AttachAsModified<T>(
this ObjectContext ctx,
string entitySet,
T entity)
{
ctx.AttachTo(entitySet, entity);
ObjectStateEntry entry =
ctx.ObjectStateManager.GetObjectStateEntry(entity);
// get all the property names
var propertyNames =
from s in entry.CurrentValues.DataRecordInfo.FieldMetadata
select s.FieldType.Name;
// mark every property as modified
foreach(var propertyName in propertyNames)
{
entry.SetModifiedProperty(propertyName);
}
}
Now you can write code like this:
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First();
// make changes to the customer in the form
ctx.AttachAsModified("Customers", customer);
ctx.SaveChanges();
And now you have no concurrency or extranous queries.
The only problem now is dealing with FK properties. You should probably look at my index of tips for help here: http://blogs.msdn.com/alexj/archive/2009/03/26/index-of-tips.aspx
Hope this helps
Alex
I suggest IEditableObject, too, and additionally IDataErrorInfo.
The way i do it is, i basically have a viewmodel for an entity that takes the entity as constructor parameter (basically a wrapper object).
In BeginEdit, i copy the entity properties to my viewmodel, so if i do CancelEdit, the data is only changed in the ViewModel and the original Entity hasn't changed. In EndEdit, i just apply the ViewModel properties to the Entity again, or course only if validation has succeeded.
For validation i use the methods of IDataErrorInfo. I just implement IDataErrorInfo.Error so that it checks each Property name via IDataErrorInfo[string columnName] and concatenates eventual error messages. If it's empty, everything is ok. (not sure if Error is meant to be used that way, but i do it)
If i have other Entities attached to my original Entity, such as Customer.Orders, i create them as nested ViewModels in the original Entity's ViewModel. The original ViewModel calls it's subModels' Begin-,Cancel-,EndEdit / Error methods in it's own implementations of those methods then.
It's a bit more work, but i think it's worth it because between BeginEdit and EndEdit, you can be pretty sure that nothing changes without you noticing it. And having a code snippet for INotifyPropertyChanged-enabled properties helps a lot, too.
The normal way of doing this is binding to something that implements IEditableObject. If and how that fits in with the entity framework, I'm not sure.