I realize there is an event ObjectMaterialized with gets called on ObjectContext after an object is materialized.
Is there a way to know when an object is currently being materialized?
An object can be re-materialized by being refreshed from the database. So I can't simply have a flag in my class indicating if I've already been materialized, because it may happen again.
Basically, when certain properties are being set, I'd like to know if they are being set as fresh values from the database (i.e. while being materialized), or if the application is calling them from elsewhere.
If i read the documentation correct then the ObjectMaterialized event fires only once when the entity object is created and then it is loaded from the database with a query or a load operation.
You can track object changes with ObjectStateManager but i don't know if it helps you find out the source of the change.
As pointed in answer by #BigL this event is not fired again when entity is refreshed. Materialization means creating an instance and that will happen only once. Refreshing only updates values in the existing instance and sets entity state.
You always know that properties are being set by refreshing because you must trigger that operation yourselves on the specified entity instance so you can control what ever flag you need to turn on or off your logic used when properties are set.
Related
Excuse me for my broken English.
In my application, all objects in the context have a property called ObsoleteFlag, which basically means if the object should still be used on the frontend. It's some sort of "soft-delete" flag without actually having to delete the data.
Now I want to prevent EF from returning any object where ObsoleteFlag is set to true (1)
If for example I retrieve object X, the navigational list property Y contains all the related objects of type Y, no matter what the ObsoleteFlag is set to.
Is there some general way of preventing EF from doing this? I don't want to check on the ObsoleteFlag property everywhere I access the context, and for every navigational property that may be loaded too.
Thanks and sorry for my broken English.
Two different approaches:
In your repository layer have a GetAllWhatever() that returns IQueryable<Whatever> and uses Where(x => !x.Obsolete) and use this whenever you retrieve objects of this type.
Create a view of Create View ActiveWhatever As Select * from ActiveWhatever Where obsolete = 0 and bind to that rather than the table.
The first is essentially checking the flag every time, but doing so in one place, so you don't have to keep thinking about it.
The second is much the same, but the work is pushed to the database instead of the .NET code. If you are going to modify the entities or add new entities you will have to make it a modifiable view, but just how that is done depends on the database in question (e.g. you can do it with triggers in SQL Server, and triggers or rules in PostgreSQL).
The second can also include having a rule or trigger for DELETE that sets your obsolete property instead of deleting, so that a normal delete as far as Entity Framework is concerned becomes one of your soft-deletes as far as the database is concerned.
I'd go for that approach unless you had a reason to object to a view existing just to help the application's implementation (that is you're heavily into the database being "pure" in being concerned with the data rather than its use). But then, if it's handy for one application it's likely handy for more, given the very meaning of this "obsolete".
I have something similar to this
var productList = order.Products.TolIst();
And I loop through the productList and update each product using
session.SaveOrUpdate(product);
But the problem is, the previous state of the product in OnFlushDirty function is null (Which make sense).
Is there anyway to manage/ copy/ inject the previous state?
Thanks
This could be related to detached objects updating. Try to read this very similar story: http://jamesfitzsimons.com/?p=152
Summary of the issue:
... On investigation we realised that the previousState parameter
passed into the onFlushDirty method of our interceptor was null. ...
The solution:
The solution was to use the merge() method (new in NHibernate 2.0).
Merge() checks the first level cache to see if an object with the
given identifier has previously been loaded. If so it loads that
object out of the first level cache and updates it’s properties using
the detached object. This means that the session is now able to track
the changes made to the object so that when the flush occurs the
previousState is no longer null.
I've an object which is called Uczestnik which just got saved to database
var konsultant = uczestnik.Konsultanci;
uczestnik.Konsultanci = null; // null attached object and reuse it's ID later on for SAVE purposes
uczestnik.KonsultantNazwa = konsultant.KonsultantNazwa;
uczestnik.Szkolenie = null; // null attached object and reuse it's ID later on for SAVE purposes
uczestnik.SzkolenieID = szkolenie.SzkolenieID;
context.SzkolenieUczestnicies.AddObject(uczestnik);
context.SaveChanges();
context.Detach(uczestnik); // detatch to prevent Context problems
uczestnik.Szkolenie = szkolenie;// reassign for use in ObjectListView
uczestnik.Konsultanci = konsultant; // reassign for use in ObjectListView
After it's saved it's back into ObjectListView where user decided to change some value and the value was changed (one value from multiple to be exact). If I check value's entity state it's in Unchanged state so calling .Attach and .SaveChanges() won't do anything. I can use ChangeObjectState but if there's nothing changed then there's no sense to do so.
context.SzkolenieUczestnicies.Attach(uczestnik);
//context.ObjectStateManager.ChangeObjectState(uczestnik, EntityState.Modified);
context.SaveChanges();
How can I detect the change and prevent unnecessary traffic (I can imagine situation where nothing is changed in the object that holds files 5mb big) so resaving it makes no sense. Unless Entity is smart enough to detect that only one field was changed from 15 and change only that field?
If the entity is detached from a context you can't find out what has changed unless you are reloading the original entity from the database or you are using self-tracking entities or manage a tracking somehow yourself.
If you reload the entity you can use ApplyCurrentValues:
var originalEntity = context.MyEntities.Single(e => e.Id == detachedEntity.Id);
context.MyEntities.ApplyCurrentValues(detachedEntity);
context.SaveChanges();
This method marks the properties as modified which have different values between original and detached entity. SaveChanges will create an UPDATE statement which includes only those changed properties. If no property did change, SaveChanges does nothing.
But you are not completely free from "unnecessary traffic" because you have to load the original entity, you will save an unnecessary UPDATE statement though.
If you Detach entity it is not tracked by the context. In such case you are responsible for detecting when the object has changed and inform the context about changes by using ChangeObjectState. So you must track what user has modified or implement something directly to your entities. For example implement INotifyPropertyChanged (if you are using EntityObject based entities this interface should be already implemented).
I have 5 entities that are loaded using the entity framework. Here they are:
All of the entities are inherited from:
(Each entity represented by a class with the properties described above. Al entities inherit Transmission entity).
As you can see, there are common properties in some of the entities. But the properties WorkerId, WorkerPersonalId, VehicleId, VehicleNumber, SubcontractorId has special methods for SET so in order to encapsulate the logic of update I created WorkerVehicleTransmission class with those properties setters implementation. Each transmission now uses the WorkerVehicleTransmission.
Now I have a new need. I need to log each property change. For that I have the Log() method. For eaxmple, I need that when the user makes cargoStorage.Weight=8; there will be a call to Log() that will log this change.
Importent issue: I need to find a solution where the creation of an entity (by the entity framework for example) will not log.
How can I integrate the new need?
This question is the real need for the example I ask about here: how to solve this code duplication + add another method
As a start you could attach a handler to the PropertyChanged event in the Transmission base class which will enable you to call the Log method whenever a property changes in any of your sub classes.
INotifyPropertyChanged.PropertyChanged Event
This however will fire when any change is made, including when the Entity Framework creates the objects, so is only half way there.
Edit
If you create a new property within the Transmission class (a boolean flag) you could use this in your data access object routines to set whether logging should be enabled.
This flag is only ever set after any Entity Framework activity on each object has been completed therefore the only Property changes logged are those relating to your code.
Not an elegant solution but I cannot see any other way.
Edit
Just had a look at the EntityObject base members and there is an Property (Enumeration) named EntityState.
EntityObject.EntityState Property
This property is set to "Detached" when the entity is being created (Unattached to the object context) by the Entity Framework and changes its value to "Added", "Deleted", "Modified" or "Unchanged" after it is added (depending on the state of the object).
By checking if the value is anything other than "Detached" you could then determine whether logging should be enabled.
If I have an object that lazy loads an association with very large objects, is there a way I can do processing at the time the lazy load occurs? I thought I could use AssociateWith or LoadWith from DataLoadOptions, but there are very, very specific restrictions on what you can do in those. Basically I need to be notified when an EntitySet<> decides it's time to load the associated object, so I can catch that event and do some processing on the loaded object. I don't want to simply walk through the EntitySet when I load the parent object, because that will force all the lazy loaded items to load (defeating the purpose of lazy loading entirely).
Subscribe to the ListChanged Event
EntitySet<T> exposes an event called ListChanged which you can use to detect if an item is being added. Evaluate the ListChangedType property of the ListChangedEventArgs. Here's a link to the values available in the ListChangedType enum.
There is no danger of forcing the load to execute as long as you avoid requesting the enumerator.
http://msdn.microsoft.com/en-us/library/system.componentmodel.listchangedtype.aspx
I don't see any extensibility points for this available; the only thing I can see is in the FK entity there is a Created method on each individual object that gets fired from the constructor...
So the constructor calls created, and personally, I'm not 100% sure that the entity set loading creates each individual object at that time, and fires the event...
HTH.
There are a bunch of built in extensibility methods to the datacontext and data classes generated by Linq2SQL.
http://msdn.microsoft.com/en-us/library/bb882671.aspx
http://csainty.blogspot.com/2008/01/linq-to-sql-extending-data-classes.html
Either of these may be able to serve the purpose you need.
You are definitely not bound to use the default EntitySet<> but can use any IList<> collection instead. I did a little reflection on EntitySet<> but have found no hook into the Load() method, which implements enumerating the lazy source of the entity set (it's where the EntitySet actually gets queried and materialized).
Linq To SQL will use the Assign() method to assign an IEnumerable source (which is lazy by default) to your collection. Starting from there, you can implement your own lazy loading EntitySet with a custom hook at the point you first enumerate the source collection (execute the query).