add log method to each setter - c#

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.

Related

Determine if the context of an entity proxy has been disposed

In a EF 6 project, I am writing validation functions for entities. some are static while others are instance methods of the entities themselves.
Ignoring whether this is bad practice or not, I'd like to check whether the entities were created using a context and if so, whether they are still attached.
Please note that these functions do NOT have access to the context object, just the entity classes.
As an example, a method validates Department entity and cascades validation to all associated Department.Employee instances.
If the hierarchy was created manually, validation will succeed.
If the hierarchy was created using a context which is still alive, validation will succeed albeit slower.
If the hierarchy was created using a context which has been disposed, validation will fail with an ObjectDisposedException (provided proxy-creation was enabled and .Include(***) was not used).
So the question, is it possible to detect the above scenarios without access to a DbContext instance? If not, how can we best validate entire hierarchies irrespective of how they were created.
var result = true;
var departments = ???; // Constructed manually or through a DbContext instance.
foreach (var department in departments)
{
result &= department.Validate();
foreach (var employee in department.Employees)
{
result &= employee.Validate();
}
}
EDIT: Please note that this is for a desktop application that cannot have long-running DbContext instances. they are almost always disposed immediately after retrieving data. Re-querying the database does not seem a viable option for validation since it is triggered by trivial user input and would slow down the entire user experience.
From your question
Please note that these functions do NOT have access to the context object, just the entity classes.
two solutions come to mind, none really palatable:
Build your own tracker and make it available to these methods somehow.
Add something to your entities, for example a WasLoaded property that gets set when you query your context. That WasLoaded could be set by either
Writing an EF interceptor that sets it.
Adding an artificial bit column with all values set to 1. Then map that to the property; the property will be false if you constructed it outside of the context, true if loaded from the context.
The tracker seems to be the cleanest because it doesn't pollute your model. The interceptor is a decent alternative if you're not concerned about your model.
And while it doesn't answer your question directly, you could avoid the use of proxies, in which case your validation works the same way regardless because you have your model in memory. There's the usual trade-offs to consider though.
I'm not sure how you'd detect the last scenario. I suppose you could have your tracker track more than the entities... have it also track the context's state.

Is it possible for a NHibernate EventListener to modify the contents of a HasMany relationship?

I am working with an application that has several domain objects with their own mapping to a similar list of HasMany domain objects. The application is running on-top of a brownfield database, so the structure cannot be easily changed. Each parent fluent mapping looks similar to:
HasMany(x => x.Locations) // Location Type applicable to the given parent
.AsBag()
.KeyColumn("parent_sk")
.LazyLoad()
.Inverse()
.Cascade.AllDeleteOrphan();
Every time a location on a parent domain object is changed the associated parent objects must have their location properties synchronized (some new locations added, some deleted, others left as is) within the same transaction.
I've created and registered an NHibernate Event Listener that implements IPreUpdateEventListener, IPreInsertEventListener, and IPreDeleteEventListener. This event listener fires as expected when a change is made to any Location domain objects.
Once fired the proper parents are found and their Location collections are manipulated in order to add/remove locations. Parents are then sent to the appropriate repository save method. I'd expect the changes to the locations collection to cascade. This however is not the case.
When debugging I've verified that the expected values are present in the collection, but nothing is modified in the database, nor is anything attempted as far as I can tell via SHOW_SQL logs. If in the process I modify a non-relationship property on the parent the changes are indeed persisted on the parent, yet the children remain unchanged.
Is there a proper way to configure a listener to modify children of a HasMany in a cascade?
I would say, that the answer could be found in this Q&A:
NHibernate IPreUpdateEventListener, IPreInsertEventListener not saving to DB
that is in fact in the Ayende's article:
NHibernate IPreUpdateEventListener & IPreInsertEventListener
...Here comes the subtlety, however. We cannot just update the entity state. The reason for that is quite simple, the entity state was extracted from the entity and placed in the entity state, any change that we make to the entity state would not be reflected in the entity itself. That may cause the database row and the entity instance to go out of sync, and make cause a whole bunch of really nasty problems that you wouldn’t know where to begin debugging.
And the description how to solve that issue:
You have to update both the entity and the entity state in these two event listeners (this is not necessarily the case in other listeners, by the way)...
A small code snippet, which must be adjusted to the above needs (handling collection elements)
public bool OnPreUpdate(PreUpdateEvent #event)
{
...
Set(#event.Persister, #event.State, "UpdatedAt", time);
...
// the way how to assure that even the State is updated
private void Set(IEntityPersister persister, object[] state
, string propertyName, object value)
{
var index = Array.IndexOf(persister.PropertyNames, propertyName);
if (index == -1)
return;
state[index] = value;
}
So, keeping both entity and entity state updated, would be the solution here.
I expect the problem is the .Inverse() statement. That basically tells NHibernate that the location is responsible for the cascading and not the parent domain object... Removing the Inverse() would make the cascading work correctly.

Entity Framework SaveChanges two behaviours depending on how I add to DbContext

I have overridden my db.SaveChanges() so I can call my FluentValidation validators before it actually attempts to save it.
I have a validator for each entity marked with IValidatableEntity and if the entity matches it will call it and pass the objectStateEntry in.
public virtual IEnumerable<string> SaveChanges(User user)
{
List<string> validationErrors = new List<string>();
if (this.Configuration.ValidateOnSaveEnabled)
{
foreach (var entry in ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager
.GetObjectStateEntries(System.Data.Entity.EntityState.Added | System.Data.Entity.EntityState.Deleted | System.Data.Entity.EntityState.Modified | System.Data.Entity.EntityState.Unchanged)
.Where(entry => (entry.Entity is IValidatableEntity)))
{
validationErrors.AddRange(((IValidatableEntity)entry.Entity).Validate(entry));
}
}
if (!validationErrors.Any())
{ .....
The problem I have is that I get two different behaviours depending on how I add the object to the dbContext. I Guess because it only marks the aggregate root as being modified and only gives it an entry?
// Example A - Calls the Organisation Validator Only
organisation.Client.Add(client);
// Example B - Calls the Client Validator - which is correct
db.Client.Add(client);
Is there anyway to get EF automatically detect child properties have changed (Add / Modified) and call them? It kind of breaks my validation model if it doesn't, I was banking on updating the aggregate root and having EF call the necessary child validations as they should have unique entries.
Do I have to chain validators inside my Fluent Validations to catch these? I Didn't want a case of where my fluent Validator will have to check potentially hundreds of child entities. (some contain db lookups etc).
Thanks
Try to call DetectChanges at the beginning of your overridden SaveChanges method (it must be before you call GetObjectStateEntries):
this.ChangeTracker.DetectChanges();
The difference between the two lines to add a Client is that organisation.Client.Add(client) does not call any EF code directly (it's just adding an item to a collection in a POCO) while db.Client.Add(client) does and the DbSet<T>.Add method will call change detection automatically to update the entity states.
In the first case if you don't call any EF method before SaveChanges the base.SaveChanges will detect the changes as the very last place to ensure that all entity states are correct and all changes are saved. But base.SaveChanges is too late for the code in your overridden SaveChanges because it is after your evaluation of GetObjectStateEntries. At that point the entity state of the added client could still be Detached (i.e. not existing in the state manager) instead of Added. In order to fix this you have to call DetectChanges manually early enough to retrieve the final entity states in GetObjectStateEntries.
I assume organisation is a simple POCO, so the following code:
organisation.Client.Add(client);
just adds another POCO in an ICollection of POCOs. EF has no way to detect you are adding an entity to the context.
In the other hand, the following code:
db.Client.Add(client);
adds a POCO directly in an implementation of ICollection (DbCollectionEntry) that is related to Entity Framework and is in charge of what's called change tracking (among other things). This is possible thanks to dynamic proxy types generated at runtime (see https://stackoverflow.com/a/14321968/870604).
So you'll have to detect changes manually (see #Slauma answer). Another option would be to use a proxy object instead of your organisation POCO. This would be possible by calling:
var newOrganisation = dbContext.Set<Organisation>().Create();
The above code of course works for a new organisation instance.

EntityFramework knowing when being materialized

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.

EF partial classes and default values

Following on from an SO dicsussion here, I have implemented partial classes so as to create default datetime values for my Created and Modified database fields in a Constructor.
Now the problem is that my database has 100+ tables, and 75+ of them have the same basic structure which includes a Created and a Modified column definition.
So.. Instead of creating 75+ partial classes which I need to maintain, is there any way I can create a base class which every EF type inherits from, which inherits the default constructor to populate the DateTime values for Created and Modified?
EDIT: Worthy of note is that I am using EF 4.0
You can certainly specify your own base class with both EF4 and EF1, though it's a lot easier with EF4. Right click on the design surface and you should see an option to add a Code Generation Item. Select the ADO.Net entity object generator. This will a T4 file to your project (.tt extension) that specifies the template to use to generate your entity classes from the model. To specify a different base class, look inside it for a line like
Private Function BaseTypeName(ByVal entity As EntityType, ByVal code As CodeGenerationTools) As String
Return If(entity.BaseType Is Nothing, "EntityObject", MultiSchemaEscape(DirectCast(entity.BaseType, StructuralType), code))
End Function
Replace EntityObject with your base class. Note that if you are using this template then your base class must inherit from System.Data.Objects.DataClasses.EntityObject - you could use a POCO template instead, but this will probably be enough for you.
You can certainly tell EF to use a base class for your entities (it's right in the designer as a property for the entity)...but if you want to make sure of the default value for this field, perhaps you could hook into the two events on your ObjectContext SavingChanges and ObjectMaterialized.
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.savingchanges.aspx
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.objectmaterialized.aspx
You could use these opportunities to inject the default value(s) that you want to use. So in your SavingChanges handler, for example, you could check the ObjectStateManager on the context to see if the state of the relevant entity is EntityState.Added, then set the Created and Modified dates as desired.
Alternatively, as suggested, is there a reason the default value for the column in SQL Server can't just be GetDate()? (Assuming you're using SQL Server)....

Categories