How can I determine which fields have changed after model has been edited
You could implement INotifyPropertyChanged on your entities. You would need fire off the PropertyChanged event for each property...so there is some refactoring you would have to do to get this working. Its the only built-in way to achieve it with .NET.
If you don't want to implement INotifyPropertyChanged manually, you could use PostSharp to update your classes and adjust your properties at compile time. However, this would require a considerably more complicated effort up front.
If you are using Linq to SQL, the GetModifiedMembers method takes an argument of tybe Object, and returns an array of System.Data.Linq.ModifiedMemberInfo objects. Every Table class in the DataContext has a GetModifiedMembers method that can be invoked on any entity.
http://msdn.microsoft.com/en-us/library/system.data.linq.itable.getmodifiedmembers.aspx
In the Entity Framework, using ObjectStateManager, one can access all this change-information like object-state (added/modified/deleted), modified properties, original and current values
IEnumerable<ObjectStateEntry> changes =
this.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Deleted | EntityState.Modified);
Related
Im trying to find out which attributes of an entity have been changed.
As far I have seen, there is a PersistenceSession with a method to check an object if an attribute isDirty. But its always true because it never registers the old object.
So if I take the demo from the QuickGuide and override the update method in the CoffeeBeanRepository:
/**
* #param \Acme\Demo\Domain\Model\CoffeeBean $coffeeBean
*/
public function update($coffeeBean) {
\TYPO3\Flow\var_dump($this->persistenceSession->isDirty($coffeeBean, 'name'), "name changed before");
parent::update($coffeeBean);
\TYPO3\Flow\var_dump($this->persistenceSession->isDirty($coffeeBean, 'name'), "name changed after");
}
... its always TRUE (both), despite I didn't change anything.
Anyone an idea/reference how this can be accomplished?
I am using it for a REST API where a user can't update several fields and on editing of some fields additional actions have to be executed.
The persistenceSession is part of the generic persistence backend of Flow and is neither maintained, nor really used unless you explicitly deactivate doctrine. Hence persistenceSession will not help you, because all entities are considered new for the persistenceSession as you noticed.
With doctrine you need to get the entity changeset from the "UnitOfWork", which you can get from an injected \Doctrine\Common\Persistence\ObjectManager. See also Is there a built-in way to get all of the changed/updated fields in a Doctrine 2 entity
However, this is a suboptimal solution and a hacky work-around at best. If you need to track changes to your entity, it should be an explicit part of your domain model. For example make your setters record a changed properties list, when the given value is different from the current.
When done, you could even optimize doctrines change tracking on the way with that: http://doctrine-orm.readthedocs.org/en/latest/reference/change-tracking-policies.html#notify
I have an aggregated data view in an MVC project which displays the totals per month broken down by audit status. The controller code sets this up using a simple LINQ projection into an anonymous object like this:
From audits In db.Audits
Group By key = audits.DateCreated.Value.Month Into g = Group
Select New With {
.Month = key,
.Assigned = g.Sum(AuditsWithStatus(AuditStatus.Issued)),
.Unassigned = g.Sum(AuditsWithStatus(AuditStatus.Pending)),
.Closed = g.Sum(AuditsWithStatus(AuditStatus.Closed)),
.Cancelled = g.Sum(AuditsWithStatus(AuditStatus.Cancelled))
}
I know this is one of the big advantages of LINQ (using anonymous types), but I don't like losing the strong typing in the view (ie #ModelType SomeStrongType). Is there any general advice on this? Articles, blogs, or other places that deal with the issue and when to use which?
You cannot do anything with anonymous types outside of the scope of your method. You cannot return them to your view for example. In those cases you have to use a known type.
I use anonymous types when I am selecting data that I am then processing in another way. For example, selecting some bespoke data out of 1 source using Linq, and putting to put into another source.
If you are returning aggregate data such as an IEnumerable<IGrouping<TKey, TValue>> and TKey and TValue are anonymous types (you can group by anonymous types if you want); then you would not want to create 2 classes for TKey and TValue, where TKey has an overridden Equals and GetHashCode so you can group by it. And then do nothing more than read some values from it and throw it away, never to be re-used.
TLDR; use them when there is no need to create a known type to store your results. If you need to pass your results to somewhere outside the scope of the method, then you will need a type.
General advice is simple: always create dedicated viewmodel type for your views. In your case it would be pretty simple, containing exactly the properties you have in you anonymous class.
I understand that it seems like an unneeded overhead, but it'll make your code more readable and verifiable.
I have an entity/table that uses sqlgeography.
Since EF 4.X doesn't support spatial types I'm instead sending the bytes of the field back and forth.
I have stored procs on the database side that handles the converstion and properties on the code side to do that job.
To add the properties in the code I used a partial class.
One of those properties is for the SqlGeography which simply wraps around the byte[] property to handle getting and setting.
This property is hidden from EF using the NotMappedAttribute.
The other is the property exposing the byte[] itself and is decorated with the EdmScalarPropertyAttribute and DataMemberAttribute.
I then go to the EF model designer (*.edmx) to point the entity model at the Insert/Update/Delete stored procs.
It finds the stored procs alright and realises that they (when appropriate) take a VARBINARY parameter.
It also has a drop down allowing you to select a property on the entity class which maps to that parameter.
However this drop down doesn't list either of my properties. I don't care about the SqlGeography property since that is meant to be hidden from EF, however it is vital for me to be able to point it at the byte[] property, as that is where the data comes from.
I would very much like to avoid database triggers or wrapper classes and addiitonal fields to fudge this in to working.
I tried manually editing the .edmx file to include the byte[] property, but then it just complains it's unmapped.
Can anyone give me some insight in to how to get this to work? Or an alternative method of achiving the end result?
We could use a view to create the binary field for us, but this then involves manually creating a lot of the xml for the relationships within the data.
This pretty much voids the point of using EF which is to make life simple and easy.
For this project We'll just add a binary field to the table then have sprocs to handle the converstion on the server and a property in a partial entity class for exposing the geography type in the model.
Next project I doubt we'll be using EF. Dapper is so much more painless, even if theres a touch more code writing involved.
Here's the links for using views if anyone thinks it would be applicable to them:
http://thedatafarm.com/blog/data-access/yes-you-can-read-and-probably-write-spatial-data-with-entity-framework/
http://smehrozalam.wordpress.com/2009/08/12/entity-framework-creating-a-model-using-views-instead-of-tables/
In the end we created a computed column for each table that exposes the spatial data as bytes.
We then use stored procs for inserting and updating the spatial data.
I am implementing OnSaveChanges() when saving an entity.
Apart from finding out all the properties of the entity that have changed how do I find out all the properties of a collection within the entity that have changed?
EG
Customer has a property
public List<Address>AddressList {get;set;}
Now if one of those addresses changes how do I detect it?
I am using the following to detect all the ObjectStateEntry modified
IEnumerable<ObjectStateEntry> changes =
stateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted);
now as a said apart from the changes to the entity I need to find all the changes to the collections the entity might have.
How do you do it?
You will need to call ObjectContext.DetectChanges() prior to calling SaveChanges(). If you don't do this, the items in the collections will not be included in GetObjectStateEntries at the time of your SavingChanges method being called.
This is a little out there but I have a customer object coming back to my controller. I want to just reconnect this object back to the database, is it even possible? I know there is a datacontext.customers.insertonsubmit(customer), but is there the equivalent datacontext.customers.updateonsubmit(customer)???
This is what I don't like about LINQ-to-SQL.
It generally works fine if you're querying and updating in the same scope, but if you get an object, cache it, and then try to update it later, you can't.
Here's what the documentation says:
Use the Attach methods with entities that have been created in one DataContext, and serialized to a client, and then deserialized back with the intention to perform an update or delete operation. Because the new DataContext has no way of tracking what the original values were for a disconnected entity, the client is responsible for supplying those values. In this version of Attach, the entity is assumed to be in its original value state. After calling this method, you can then update its fields, for example with additional data sent from the client.
Do not try to Attach an entity that has not been detached through serialization. Entities that have not been serialized still maintain associations with deferred loaders that can cause unexpected results if the entity becomes tracked by a second data context.
A little ambiguous IMHO, specifically about exactly what it means by "serialized" and "deserialized".
Also, interestingly enough, here's what it says about the DataContext object:
In general, a DataContext instance is
designed to last for one "unit of
work" however your application defines
that term. A DataContext is
lightweight and is not expensive to
create. A typical LINQ to SQL
application creates DataContext
instances at method scope or as a
member of short-lived classes that
represent a logical set of related
database operations.
So, DataContexts are intended to be tightly scoped - and yet to use Attach(), you have to use the same DataContext that queried the object. I'm assuming/hoping we're all completely misunderstanding what Attach() is really intended to be used for.
What I've had to do in situations like this is re-query the object I needed to update to get a fresh copy, and then do the update.
The customer that you post from the form will not have entity keys so may not attach well, also you may not have every field of the customer available on the form so all of it's fields may not be set.
I would recommend using the TryUpdateModel method, in your action you'll have to get the customer from the database again and update it with the form's post variables.
public ActionResult MySaveAction(int id, FormCollection form)
{
Customer updateCustomer = _Repository.GetCustomer(id);
TryUpdateModel(updateCustomer, "Customer", form);
_Repository.Save(updateCustomer);
}
You will have to add in all your own exception handling and validation of course, but that's the general idea.
You want to use the attach method on the customers table on the data context.
datacontext.customers.Attach(customer);
to reconnect it to the data context. Then you can use SubmitChanges() to update the values in the database.
EDIT: This only works with entities that have been detached from the original data context through serialization. If you don't mind the extra call to the database, you can use the idiomatic method in ASP.NET MVC of retrieving the object again and applying your changes via UpdateModel or TryUpdateModel as #Odd suggests.