NHibernate: Find dirty object in child collection - c#

I try to find out how to identify that parent object is Dirty when one of the objects in the child collection was changed by using the features of the NHibernate context.
I mean for the following case:
public class Parent
{
public IList<Child> Childs { get; set; }
}
public class Child
{
public String Name {get; set; }
}
...
var parent = session.Get<Parent>(1);
parent.Childs[0].Name = "new name";
// here <code>session.IsEntityDirty(parent)</code> should return true
I know about extensions to the ISession like here http://nhibernate.info/doc/howto/various/finding-dirty-properties-in-nhibernate.html and here for collection NHibernate: Find dirty collections. But neither first (it tracks just properties), nor second (it tracks just operations add/delete on collection object) work.
I want also to mention that I prefer to use plain POCOs instead of STEs.
I would be very appreciate for a solution.

It does not look if NHibernate is checking if the the object graph is dirty but on the entity itself. So it ignores collections and other references.
If you follow the code
EntityEntry oldEntry = persistenceContext.GetEntry(entity);
Object[] oldState = oldEntry.LoadedState;
The oldState only includes the original values of the entity, but includes the current values of the collection. So my guess is that NHibernate does not take that into account but does another loop somewhere to loop through those entities in the list and check them separately.
The way I would do it is by looping through the properties of the entity and check that with session.IsDirtyProperty skipping over collections and other entity type properties, but then looping through collections and for each entity in the collection checking against that entity's properties.
Remember that your session will need to stay open and not flushed for this to work before checking, if the entity gets detached from the session there won't be a way to use NHibernate to check if it is dirty.
It is also not recommended to keep a session open for a long time, you should use it and dispose it when you done.

Related

How can I attach an entity to the context when one of its children is already attached?

Background
In my application we were running into issues when trying to add a new entity with existing children to the database after mapping it from a DTO using AutoMapper. The entity and its children were not attached to the context, and so the children would be treated as new and EF would attempt to insert duplicates. My solution to this was to automatically attach an entity to the context whenever an object was mapped to a BaseEntity type (BaseEntity is the base class for all of our Model objects, it has an Id property and nothing else). Here is the code:
public TDestination Map<TDestination>(object source) where TDestination : class
{
var result = _mapper.Map<TDestination>(source);
if (typeof(TDestination).IsSubclassOf(typeof(BaseEntity)) && result != null)
_context.Attach(result); //_context is a DbContext
return result;
}
This worked fine in my initial test cases, but now I've run into an issue where the entity I'm attaching has a child that is already attached to the context. This throws "The instance of entity type 'MyChildEntity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.".
How can I attach an entity to the context when a child is already attached? I'm trying to keep this method extremely generic so that it can be used by any object that we are trying to map from a DTO to a BaseEntity.
What I've Tried
I've tried grabbing the associated EntityEntry and recursively detach all of its children using the following method before attempting to call Attach():
private void DetachChildren(EntityEntry entity)
{
foreach (var member in entity.Members.Where(x => x.CurrentValue != null))
{
if (IsBaseEntityType(member.CurrentValue.GetType()))
{
var childEntity = _context.Entry(member.CurrentValue);
childEntity.State = EntityState.Detached;
DetachChildren(childEntity);
}
}
}
I've confirmed that this loop does reach the problem child and sets its state to detached, but I still end up getting the same error when calling Attach().
Welcome to the hell that is working with detached entities.
Automapper can be leveraged to update existing entities rather than forming an entity and attaching it. Given an object like an Order:
public void UpdateOrder(OrderDTO orderDTO)
{
var order = _context.Orders.Single(x => x.OrderId = orderDTO.OrderId);
_mapper.Map(orderDTO, order);
_context.SaveChanges();
}
The benefits of this approach is that it handles whether the order happens to be tracked or not, asserts the order exists for something like an Update where it is assumed it does, and when SaveChanges runs, only the fields that actually changed will be updated. If only 1 field changed, the update statement updates that single field. Attaching a new object and setting EntityState to Modified will update all fields. This could introduce unexpected attack vectors to change data you don't expect since a DTO needs to pass enough info to construct a whole entity to avoid unintentionally #null-ing data. The mapping from DTO to entity should ensure that only editable fields are copied across.
In the case where the OrderDTO will contain one or more child collections to update, you will likely need to use a mapping that excludes those collections, then use AfterMap in the mapping configuration to inspect the child collection for new vs. existing vs. removed entities and handle those accordingly. (Add vs. mapper.Map vs. Remove)
Generally the updates can be structured to perform atomic operations that make the entity interactions as straight forward as possible. For instance UpdateOrderDetails(orderDTO) would update information about the order itself, where there would be separate methods such as AddOrderLine(newOrderLineDTO) vs. UpdateOrderLine(orderLineDTO) vs. RemoveOrderLine(orderLineId) etc. rather than having all order line operations and other related changes done through a single UpdateOrder method accepting a whole modified object graph.
The alternative when dealing with graphs and the possibility of tracked entities is that you need to check each and every related entity against the DbSet's .Local or other means to check to see if the entity is tracked. If it is, then you have to replace the references and copy any applicable changes to the already tracked entity. Telling a DbContext to ignore an existing entity isn't always a simple matter as there can be references to that entity in other tracked entities. Generally you'll want to detect a tracked entity reference then update your references to use that tracked reference and update it as needed. It is lots of mucking around with references, and definitely does not work well with Generic methods
Generic operations are tempting from a DNRY standpoint, but dealing with situations where some entities might be tracked vs. not, and then handling type mismatches etc. (source = object = can be anything..) adds a lot of complexity in place of simpler methods to handle operations.

Generic way for checking related entities count in Entity Framework

Currently I am using this code:
opportunity.Contacts.Where(x => x.IsDeleted = false).IsNullOrEmpty()
in each entity to check if any collection exists in the entity (example Opportunity).
public bool Delete(int companyId, int opportunityId)
{
var opportunity = _opportunityRepository.FindOne(companyId: companyId, opportunityId: opportunityId).FirstOrDefault();
if (!opportunity.Contacts.Where(x => x.IsDeleted = false).IsNullOrEmpty())
{
throw new UserFriendlyException(ErrorMessages.UserFriendly.UnableToDeleteEntityHasRelatedData);
}
opportunity.IsDeleted = true;
_opportunityRepository.Edit(opportunity);
CurrentUnitOfWork.Commit();
return true;
}
This method is repetitive and time consuming to put in hundreds of places.
How can we make it a generic function which can check entity type and use reflection or another way to check all its properties which implement ICollection<T> and execute query to check their count?
[ForeignKey("DepartmentId")]
public virtual ICollection<DepartmentLocation> DepartmentLocations { get; set; }
[ForeignKey("DepartmentId")]
public virtual ICollection<EmployeePosition> EmployeePositions { get; set; }
Even if you did want to go through and resolve collections by reflection to perform a check like this, I would not recommend it. The problem domain you are looking at looks like that you want to enforce business logic around soft deletion that essentially says "I can only be marked as deleted if all of my children are first marked as deleted." The issues you would quickly face with trying to do this through reflection include:
Hits to Lazy Loading
Complex and slow code to inspect every object on delete.
The lazy loading risk and trying to escape it would be my first warning of dragons. I'd be looking at leveraging IQueryable from the repository rather than returning a single domain object. From there you have the flexibility to drill down into your model to determine if an object has any active children:
var activeState = _opportunityRepository.GetById(companyId, opportunityId) // return IQueryable<Opportunity>
.Select(o=> new {o.IsDeleted, HasActiveContact = o.Contacts.Any(c=> !c.IsDeleted)})
.SingleOrDefault();
From there you can check properties on the anon. type. The query sent to the server should remain pretty optimal in terms of performance with a single hit and the code is a simple, extendable structure that you can expand upon easily without worrying about lazy loads and such. You can expand it to return active children for instance if you want to extend a message about which children are still active to deal with before marking the parent as deleted.

Saving Database Entities

I have a C# program that loads a list of products from a database into a list of Product objects. The user can add new products, edit products, and delete products through my program's interface. Pretty standard stuff. My question relates to tracking those changes and saving them back to the database. Before I get to the details, I know that using something like Entity Framework or NHiberate would solve my problem about tracking adds and edits, but I don't think it would solve my problem about tracking deletes. In addition to wanting an alternative to converting a large codebase to using Entity Framework or NHiberate, I also want to know that answer to this question for my own curiosity.
In order to track edits, I'm doing something like this on the Product class where I set the IsDirty flag any time a property is changed:
class Product
{
public bool IsDirty { get; set; }
public bool IsNew { get; set; }
// If the description is changed, set the IsDirty property
public string Description
{
get
{
return _description;
}
set
{
if (value != _description)
{
this.IsDirty = true;
_description = value;
}
}
}
private string _description;
// ... and so on
}
When I create a new Product object, I set its IsNew flag, so the program knows to write it to the database the next time the user saves. Once I write a product to the database successfully, I clear its IsNew and IsDirty flags.
In order to track deletes, I made a List class that tracks deleted items:
class EntityList<T> : List<T>
{
public List<T> DeletedItems { get; private set; }
EntityList()
{
this.DeletedItems = new List<T>();
}
// When an item is removed, track it in the DeletedItems list
public new bool Remove(T item)
{
this.DeletedItems.Add(item);
base.Remove(item);
}
}
// ...
// When I work with a list of products, I use an EntityList instead of a List
EntityList<Product> products = myRepository.SelectProducts();
Each time I save a list of products to the database, I iterate through all of the products in the EntityList.DeletedItems property and delete those products from the database. Once the list is saved successfully, I clear the DeletedItems list.
All of this works, but it seems like I may be doing too much work, especially to track deleted items and to remember to set the IsNew flag every time I create a new Product object. I can't set the IsNew flag in Product's constructor because I don't want that flag set if I'm loading a Product object from the database. I'm also not thrilled with the fact that I have to use my EntityList class everywhere instead of using List.
It seems like this scenario is extremely common, but I haven't been able to find an elegant way of doing it through my research. So I have two questions:
1) Assuming that I'm not using something like Entity Framework, is there a better way to track adds, edits, and deletes and then persist those changes to the database?
2) Am I correct in saying that even when using Entity Framework or NHiberate, that I'd still have to write some additional code to track my deleted items?
In EF the DbContext object contains all of the logic to track changes to objects that it knows about. When you can SaveChanges it figures out which changes have happened and performs the appropriate actions to commit those changes to the database. You don't need to do anything specific with your object state other than inform the DbContext when you want to add or remove records.
Updates:
When you query a DbSet the objects you get are tracked internally by EF. During SaveChanges the current state of those objects are compared against their original state and those that are changed are put into a queue to be updated in the data.
Inserts:
When you add a new object to the relevant DbSet it is flagged for insertion during the SaveChanges call. The object is enrolled in the change tracking, it's DB-generated fields (auto-increment IDs for instance) are updated, etc.
Deletes:
To delete a record from the database you call Remove on the relevant DbSet and EF will perform that action during the next SaveChanges call.
So you don't need to worry about tracking those changes for the sake of the database, it's all handled for you. You might need to know for your own benefits - it's sometimes nice to be able to color changed records for instance.
The above is also true for Linq2SQL and probably other ORMs and database interface layers, since their main purpose is to allow you to access data without having to write reams of code for doing things that can be abstracted out.
is there a better way to track adds, edits, and deletes and then persist those changes to the database?
Both Entity Framework and NHibernate chose not to make entities themselves responsible for notifying nor tracking their changes*. So this can't be a bad choice. It certainly is a good choice from a design pattern's point of view (single responsibility).
They store snapshots of the data as they are loaded from the database in the context or session, respectively. Also, these snapshots have states telling whether they are new, updated, deleted or unchanged. And there are processes to compare actual values and the snapshots and update the entity states. When it's time to save changes, the states are evaluated and appropriate CRUD statements are generated.
This is all pretty complex to implement all by yourself. And I didn't even mention integrity of entity states and their mutual associations. But of course it's doable, once you decide to follow the same pattern. The advantage of the data layer notifying/tracking changes (and not the entities themselves) is that the DAL know which changes are relevant for the data store. Not all properties are mapped to database tables, but the entities don't know that.
I'd still have to write some additional code to track my deleted items?
No. Both OR mappers have a concept of persistence ignorance. You basically just work with objects in memory, which may encompass removing them from a list (either nested in an owner entity or a list representing a database table) and the ORM knows how to sync the in-memory state of the entities with the database.
*Entity Framework used to have self-tracking entities, but they were deprecated.

Is there a design pattern for light & heavy versions of an object?

I have the need for both light-weight, and heavy-weight versions of an object in my application.
A light-weight object would contain only ID fields, but no instances of related classes.
A heavy-weight object would contain IDs, and instances of those classes.
Here is an example class (for purpose of discussion only):
public class OrderItem
{
// FK to Order table
public int OrderID;
public Order Order;
// FK to Prodcut table
public int ProductID;
public Product Product;
// columns in OrderItem table
public int Quantity;
public decimal UnitCost;
// Loads an instance of the object without linking to objects it relates to.
// Order, Product will be NULL.
public static OrderItem LoadOrderItemLite()
{
var reader = // get from DB Query
var item = new OrderItem();
item.OrderID = reader.GetInt("OrderID");
item.ProductID = reader.GetInt("ProductID");
item.Quantity = reader.GetInt("Quantity");
item.UnitCost = reader.GetDecimal("UnitCost");
return item;
}
// Loads an instance of the objecting and links to all other objects.
// Order, Product objects will exist.
public static OrderItem LoadOrderItemFULL()
{
var item = LoadOrderItemLite();
item.Order = Order.LoadFULL(item.OrderID);
item.Product = Product.LoadFULL(item.ProductID);
return item;
}
}
Is there a good design pattern to follow to accomplish this?
I can see how it can be coded into a single class (as my example above), but it is not apparent in which way an instance is being used. I would need to have NULL checks throughout my code.
Edit:
This object model is being used on client side of client-server application. In the case where I'm using the light-weight objects, I don't want lazy load because it will be a waste of time and memory ( I will already have the objects in memory on client side elsewhere)
Lazy initialization, Virtual Proxy and Ghost are three implementations of that lazy loading pattern. Basically they refer to load properties once you need them. Now, I suppose you'll be using some repo to store objects so I'll encourage you to use any of the ORM tools available. (Hibernate, Entity Framework and so on), they all implement these functionality free for you.
Have you considered using an ORM tool like NHibernate for accessing DB? If you use something like NHibernate, you would get this behavior by means of lazy loading.
Most ORM tools do exactly what you are looking for within lazy loading - they first get the object identifiers, and upon accessing a method, they issue subsequent queries to load the related objects.
Sounds like you might have a need for a Data Transfer Object (DTO), just a "dumb" wrapper class that summarizes a business entity. I usually use something like that when I need to flatten out an object for display. Be careful, though: overuse results in an anti-pattern.
But rendering an object for display is different from limiting hits against the database. As Randolph points out, if your intention is the latter, then use one of the existing deferred loading patterns, or better yet, use an ORM.
Take a look at the registry pattern, you can use it to find objects and also to better manage these objects, like keeping them in a cache.

NHibernate - load sql query to entity

Basically, I need to set a property to the results of a query that uses data from the parent object.
With the domain model below, I need to set the C property of EntityB using data from both EntityA and EntityB.
Also, I need to set the A property of EntityB to be the actual instance of EntityA that is its parent.
Query:
Set EntityB.C = (select * from EntityC where SomeProperty = EntityB.SomeProperty and AnotherProperty = EntityB.A.AnotherProperty);
SomeProperty and AnotherProperty are not just keys.
class EntityA
{
public IList<EntityB> B
{
get;
set;
}
}
class EntityB
{
public EntityA A
{
get;
set;
}
public EntityC C
{
get;
set;
}
}
class EntityC
{
...
}
I need a way to execute code (to run the query and assign to property) for each entity returned. I came close using the onload method of an interceptor, but I am looking for another way. Perhaps using a Result Transformer or a Projection?
First of all, if you're using NHibernate properly, the properties and associations should be automatically done for you by the framework. If they're not, then you don't have it set up correctly...
As for doing a query in a property... this is usually not recommended (abstract it into a utility class, or at the very least a function call), but I do remember seeing some way on here how to do it.
There are actually two questions.
Question 1: How to have a property that is loaded by some query?
Ask your self if it really needs to be in the entity. Consider to have a DTO (data transfer object) that holds data from different entities and queries instead.
If you're sure that you need this property in the entity, take a look at formulas for single ended properties and filters for collections.
I can't provide more detailed information, because your question is highly general, and it depends on the actual problem. But you should find a solution by starting with the given links.
Question 2: How can I have a property pointing to the parent?
Very easy: By just implementing the property and map the collection of children (B) "inverse=true". Implement your entities the way that they consistently point to the correct parent.
Why is NH not doing this for you? Because NH's responsibility is only to persist your entities to the database. NH does not make any changes on the data by its own. This is responsibility of your business logic.
Note: your application should also be able to run without NH, eg in a unit test. So relations should be managed in your code.

Categories

Resources