Auditing child entities while saving parent entity in neo4j - neo4j

I have a parent child relationship in neo4j and I want to do a save on the parent entity which will implicitly save the child entity
If the entity extends AuditEntity, then I want to set the createTimestamp and updateTimestamp before saving the parent and child entity
To achieve this I was thinking of adding BeforeSaveEvent Application Listner and setting the values there. But this will set the values only for the parent entity. How can I set them on the child entities ?
Or is there any better approach to do this ?
I am using SDN 4.1.2
#Bean
ApplicationListener<BeforeSaveEvent> beforeSaveEventApplicationListener() {
return new ApplicationListener<BeforeSaveEvent>() {
#Override
public void onApplicationEvent(BeforeSaveEvent event) {
if(event.getEntity() instanceof AuditEntity) {
(AuditEntity) auditEntity = (AuditEntity)event.getEntity();
auditEntity.setCreateTimeStamp(new Timestamp((new Date()).getTime()));
auditEntity.setUpdateTimeStamp(new Timestamp((new Date()).getTime()));
}
}
};
}

Related

Constructors and Methods on POCO classes with the IRepository Pattern

Is it okay to have a constructor or other non database accessing methods on POCO classes. For example when passing a view model to a controller.
Controller:
public ActionResult SomeMethod(SomeViewModel model)
{
var entity = new SomePocoClasse(model);
// ... then save entity to database
return SomeActionResult
}
Entity:
public SomeClass() {}
public SomeClass(SomeViewModel model)
{
// create itself based on values in model
}
public void Update(SomeViewModel model)
{
// Update itself base on values in model
}
The first entity constructor is for entity framework or regular creation
var entity = new entity
{
// set up properties
};
The second is for creation from SomeViewModel
var entity = new entity(SomeViewModel);
The method is for updating itself from SomeViewModel
var entity = SomeIRepository.Get(id);
entity.Update(SomeViewModel);
Or is the above bad practice which should go some where else.
Yes and no. In general, it's not necessarily bad practice to have a constructor on a POCO. There's any number of reasons why you might want or need that. However, you need to ensure that you maintain a parameterless constructor as well, or you'll cause issues with things like EF which won't know how to properly initialize your POCO class otherwise.
That said, what you're doing here is not good practice. You haven't provided a ton of code, but it appears that what you're doing is passing in the view model to the POCO constructor to set the properties on the POCO with those values. Rather, what you should be doing is pulling the entity fresh from the database and then mapping over any relevant properties on your view model to that entity instance. I supposed what you're doing could be fine solely when creating a new entity, but that means having two separate ways of populating your POCO class with values depending on whether you're creating or editing. That increases complexity and complexity means higher maintenance costs.
Instead, you should either use a library like AutoMapper or create a utility class to handle the mapping:
public static class SomePocoMapper
{
public static SomePoco Map(SomeViewModel model)
{
return Map(model, null);
}
public static SomePoco Map(SomeViewModel model, SomePoco entity)
{
entity = entity ?? new SomePoco();
// map over property values;
return entity;
}
}
Then in your create action:
var entity = SomePocoMapper.Map(model);
And in your edit action:
var entity = // get entity from database
SomePocoMapper.Map(model, entity);

Neo4j SDN UUID Transaction Handler

I'm using Neo4j 2.1.7 with SDN 3.3.0.RELEASE in embedded mode.
How can i automatically assign UUIDs to new nodes? What is the best practice when trying to achieve this?
Current Solution:
I added a TransactionEventHandler which automatically add a uuid property to every node. Additionally this handler also blocks the commit when someone tries to remove the uuid.
Problems:
With SDN i always have to reload the entity outside of my transaction in order to use the generated uuid. This limits the usefulness of #Transactional. Is there maybe a way to workaround this?
You have several posibilities
Assign the uuid in entity constructor, the advantage of this is that you can then use the uuid safely in equals and hashCode.
MyNodeEntity() {
uuid = UUID.randomUUID.toString()
....
}
Use lifecycle events, very useful for cases similar to "add uuid
if it is not set"
#Bean
ApplicationListener<BeforeSaveEvent> beforeSaveEventApplicationListener() {
return new ApplicationListener<BeforeSaveEvent>() {
#Override
public void onApplicationEvent(BeforeSaveEvent event) {
MyNodeEntity entity = (MyNodeEntity) event.getEntity();
if (!entity.hasUuid()) {
entity.setUuid(UUID.randomUUID.toString());
}
}
};
}
Subclass GraphRepository and override save() method - too complicated for something that can be achieved using the lifecycle events
I just enhanced my save method in my entity service implementation which will set the uuid upfront when it determined that the given entity is new.
Entity:
public boolean isNew() {
return null == getId();
}
EntityServiceImpl:
#Override
public T save(T node) {
if (node.isNew() && node.getUuid() == null) {
node.setUuid(UUIDTransactionEventHandler.getUUID());
}
return nodeRepository.save(node);
}

Repository pattern giving exception while updating record

In my MVC application, I have been using Repository pattern for DAL.
Now, when I do select one entity record and and update the entity field value and do Update operation then getting below error.
Attaching an entity of type 'DAL.User' failed because another entity
of the same type already has the same primary key value. This can
happen when using the 'Attach' method or setting the state of an
entity to 'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate."} System.Exception
Below is repository stuff:
public void Update(TEntity entity)
{
if (_context.Entry(entity).State != EntityState.Modified)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
Calling as follow:
In Bussines layer library:
Manager class :
private readonly IUnitOfWork _unitOfWork;
private IRepository <User , int> UserRepository
{
get
{
return _unitOfWork.GetRepository<AccountUser, int>();
}
}
public void UpdateUserEntity(UserDTO u)
{
try
{
User model = new User ();
UserRepository.Update(Mapper.Map(u, model));
_unitOfWork.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
Please guide me how I could resolve above error.
The exception says that there is another entity with the same key that has been attached, but different reference.
The exception could be caused by previous attached entity.
db.Set<Entity>().Attach(new Entity { Id = 123 });
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
Or could be also caused by tracked entity that automatically attached.
db.Set<Entity>().FirstOrDefault(e => e.Id == 123); // automatically attached
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
The second cause can be solved by mentioning AsNoTracking when retrieving item.
db.Set<Entity>().AsNoTracking().FirstOrDefault(e => e.Id == 123);
Or to be safe you can use this extension to always detach any attached entity.
public static class DbSetExtension
{
public static void SafeAttach<T>(
this DbContext context,
T entity,
Func<T, object> keyFn) where T : class
{
var existing = context.Set<T>().Local
.FirstOrDefault(x => Equals(keyFn(x), keyFn(entity)));
if (existing != null)
context.Entry(existing).State = EntityState.Detached;
context.Set<T>().Attach(entity);
}
}
Usage.
db.SafeAttach(entity, e => e.Id);
It's because of the reason,
"TEntity entity as a new object instead of the one which already exists".
Means,Entity framework treats each new object as new entry.(eventhough with same existing old data,PK & all).
Solution is,
First retrieve the object from database
Do/assign the changes to the same object (preferably without changing Primary key)
Then do state as Modified ,Update,SaveChange()

Getting Entity Framework to cascade-delete when re-attaching modified entity

I'm developing a website using MVC4 with EF code first approach.
I have some problems with deleting the children in entities with one to many relations.
Edit to clearify: In my Edit view, I add/remove/update existing childen in the childrens collection on the parent, add/remove is done using javascript. When I receive the updated parent in the post request in the controller method, I want to sync/update the parent and child entities in the database.
The parent object is in a detached state when updated in the view. So, when I attach the parent again, I want it to do all the updates that have been done during detached state.
The entity relations are set up so that when removing a child entity from the parent collection, the child entity is also deleted from the child table (cascade delete sort of?), and this works when in attached state.
However, when attching the parent and saving the changes, only added/updated children are added/modified in the database. But removed children from the parent collection are not deleted in the database (which I want them to be).
How can this be solved??
The entities are:
class Parent
{
public virtual ICollection<Child> Children { get; set; }
}
class Child
{
public string Text { get; set; }
}
This works and will remove child from database:
void RemoveChildFromCollection()
{
// get the first parent and remove the first child in collection
var context = new DatabaseContext();
var parent = context.Parents.First();
parent.Children.Remove(parent.Children.First());
context.SaveChanges();
}
ControllerMethod: This does not work as above, removed children are not removed from the childrens table
public ActionResult Edit(Parent parent)
{
var context = new DatabaseContext();
context.Entry(parent).State = System.Data.EntityState.Modified;
context.SaveChanges();
return View();
}
The modelbuilder are setup to delete child entity from child table when removing them from parent collection
// Use Identifying relation. Define complex key for ChildObject containing both Id and
ParentObjectId
modelBuilder.Entity<Child>()
.HasKey(c => new {c.ChildID, c.ParentID});
// Because defining such key will remove default convention for auto incremented Id you must redefine it manually
modelBuilder.Entity<Child>()
.Property(c => c.ChildID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Set cascade delete
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired()
.HasForeignKey(c => c.ParentID)
.WillCascadeOnDelete();
Cascade delete only deletes child entities, when the parent entity is also removed, as you've noted, not when you sever the relationship.
You can override your SaveChanges() in your Context to clean up your orphaned Child entities like this:
public override int SaveChanges()
{
Children
.Local
.Where(c => c.Parent == null)
.ToList()
.ForEach(child => Children.Remove(child));
return base.SaveChanges();
}
This blog post has more info on handling orphaned entities.
Your entities - parent and children - are in a detached state when you do the modification in the view. So, EF couldn't track those changes. When you attach the object graph to the context - by setting the parent's state to Modified - EF takes this attached object graph as the current state and doesn't know anything about the removal of the child that happened during the detached phase in the view.
To solve the problem you must load the current object graph - parent including children - from the database, compare it with the object graph from your view and merge the changes into the loaded graph. Then save the changes. There can be several possible changes:
The parent's scalar properties could have been changed
The children's scalar properties can been changed
A new child could have been added to the Children collection
A child could have been removed from the Children collection
Your current code - setting the parent's state to Modified - will only handle the first case correctly but not the other three cases.
To deal with all four cases you need to follow the procedure described above. An example how to do that is shown here (see the Edit section in that answer).
The code in your Edit post action would then look like this:
public ActionResult Edit(Parent parent)
{
using (var context = new DatabaseContext())
{
var parentInDb = context.Parents
.Include(p => p.Children)
.Single(p => p.ParentId == parent.ParentId);
context.Entry(parentInDb).CurrentValues.SetValues(parent);
foreach (var childInDb in parentInDb.Children.ToList())
if (!parent.Children.Any(c =>
c.ChildId == childInDb.ChildId &&
c.ParentId == childInDb.ParentId)) // or == parent.ParentId
context.Children.Remove(childInDb);
// here
// parentInDb.Children.Remove(childInDb);
// should work too because you have an identifying relationship
foreach (var child in parent.Children)
{
var childInDb = parentInDb.Children.SingleOrDefault(c =>
c.ChildId == child.ChildId &&
c.ParentId == child.ParentId); // or == parent.ParentId
if (childInDb != null)
context.Entry(childInDb).CurrentValues.SetValues(child);
else
parentInDb.Children.Add(child);
}
context.SaveChanges();
return View();
}
}

Find Parent EntityKey and Type in Entity Framework

Is it possible to get the EntityKey and type of an entity's parent entity without knowing the type? I've tried doing the following
public partial class TestEntities
{
partial void OnContextCreated()
{
this.SavingChanges += new EventHandler(logChanges);
}
void logChanges(object sender, EventArgs e)
{
IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(
EntityState.Added |
EntityState.Deleted |
EntityState.Modified);
TestEntities context = sender as TestEntities;
foreach (ObjectStateEntry stateEntryEntity in changes)
{
if (!stateEntryEntity.IsRelationship && stateEntryEntity.Entity != null)
{
Audit audit = new Audit
{
AuditID = Guid.NewGuid()
};
foreach (var relationship in stateEntryEntity.RelationshipManager.GetAllRelatedEnds())
{
var parent = stateEntryEntity.RelationshipManager.GetRelatedCollection<EntityObject>(relationship.RelationshipName, relationship.TargetRoleName);
audit.Decription =
string.Format("{0} changed on {1} with id of {2}",stateEntryEntity.Entity, parent.GetType().Name);
}
context.AddToAudits(audit);
}
}
}
}
But I get the following.
An EntityCollection of EntityObject objects could not be returned for role name
'Canine' in relationship 'TestModel.FK_CANINE'. Make sure that the
EdmRelationshipAttribute that defines this relationship has the correct
RelationshipMultiplicity for this role name. For more information, see the
Entity Framework documentation.
I'm wondering if maybe I'm approaching this the worng way.
After search it isn't feasible to do what I wanted with EF. I wanted to crawl the inheritance tree for Auditing purposes. I ended up creating an Auditor interface that logs an audit message. I created Auditor implementations for the each EF entity type I wanted to audit. When the calling code asked for a class that implemented the Auditor interface I used named mappings from Microsoft Unity to inject the concrete class or avoid auditing if no mapping was found.

Resources