Change tracking aggregate root in DDD - nhibernate

This question is largely based on the article NHibernate – Automatic change tracking for aggregate roots in DDD scenarios
Although the logic in the article seems sound I have yet to find an implementation solution that will cover all use cases.
The problem seems to be related to the following paragraph from the article
There is a slight problem here that we may generate several updates per transaction here, but I am not worried about that overly much, it is fairly simple to resolve (by keeping track of the entity and not updating if we already updated in the current transaction), so I’ll leave it up to you.
Following the article we simply force a version update whenever we update a related entity within the aggregate root. However in cases where both aggregate root and related entity are 'dirty' this will cause a double update on the aggregate root. This causes nhibernate to fall over as the second version update triggered by default from the dirty aggregate root expects the version to be the same as what was loaded from the db.
I've attempted to put a check into the 'PreInsertEventListener' and 'PreUpdateEventListener' checking if the aggregate root is dirty when updating a related entity. If this is the case then ignore the forced update of version.
public bool OnPreUpdate(PreUpdateEvent updateEvent)
{
var rootFinder = updateEvent.Entity as ICanFindMyAggregateRoot;
if (rootFinder == null)
return false;
if (!updateEvent.Session.IsAggregateRootDirty(rootFinder.MyRoot))
{
updateEvent.Session.Lock(rootFinder.MyRoot, LockMode.Force);
}
return false;
}
public static class SessionExtensions
{
public static bool IsAggregateRootDirty(this ISession session, IAggregateRoot entity)
{
ISessionImplementor sessionImplementation = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImplementation.PersistenceContext;
IEntityPersister entityPersister = sessionImplementation.GetEntityPersister(null, entity);
EntityEntry entityEntry = persistenceContext.GetEntry(entity);
if ((entityEntry == null) && (entity is INHibernateProxy))
{
INHibernateProxy proxy = entity as INHibernateProxy;
object obj = sessionImplementation.PersistenceContext.Unproxy(proxy);
entityEntry = sessionImplementation.PersistenceContext.GetEntry(obj);
}
object[] oldState = entityEntry.LoadedState;
object[] currentState = entityPersister.GetPropertyValues(entity, sessionImplementation.EntityMode);
int[] findDirty = entityEntry.Persister.FindDirty(currentState, oldState, entity, sessionImplementation);
var hasDirtyCollection = currentState.OfType<IPersistentCollection>().Any(x => x.IsDirty);
return (findDirty != null) || hasDirtyCollection;
}
}
This solution does seem to work albeit I still need to test it with few more use cases. However I feel as if this solution is a bit heavy handed and was hoping for a solution more along what was outlined in the article.
Is there a way to detect weather the version has already been updated in the same transaction or will be, or a simple way to keep track of entities within the transaction set have its version updated.
Thanks.

Related

NHibernate not persisting changes to my object

My ASP.NET MVC 4 project is using NHibernate (behind repositories) and Castle Windsor, using the AutoTx and NHibernate Facilities. I've followed the guide written by haf and my I can create and read objects.
My PersistenceInstaller looks like this
public class PersistenceInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.AddFacility<AutoTxFacility>();
container.Register(Component.For<INHibernateInstaller>().ImplementedBy<NHibernateInstaller>().LifeStyle.Singleton);
container.AddFacility<NHibernateFacility>(
f => f.DefaultLifeStyle = DefaultSessionLifeStyleOption.SessionPerWebRequest);
}
}
The NHibernateInstaller is straight from the NHib Facility Quickstart.
I am using ISessionManager in my base repository...
protected ISession Session
{
get
{
return _sessionManager.OpenSession();
}
}
public virtual T Commit(T entity)
{
Session.SaveOrUpdate(entity);
return entity;
}
Finally, my application code which is causing the problem:
[HttpPost]
[ValidateAntiForgeryToken]
[Transaction]
public ActionResult Maintain(PrescriberMaintainViewModel viewModel)
{
if (ModelState.IsValid)
{
var prescriber = UserRepository.GetPrescriber(User.Identity.Name);
//var prescriber = new Prescriber { DateJoined = DateTime.Today, Username = "Test" };
prescriber.SecurityQuestion = viewModel.SecurityQuestion;
prescriber.SecurityAnswer = viewModel.SecurityAnswer;
prescriber.EmailAddress = viewModel.Email;
prescriber.FirstName = viewModel.FirstName;
prescriber.LastName = viewModel.LastName;
prescriber.Address = new Address
{
Address1 = viewModel.AddressLine1,
Address2 = viewModel.AddressLine2,
Address3 = viewModel.AddressLine3,
Suburb = viewModel.Suburb,
State = viewModel.State,
Postcode = viewModel.Postcode,
Country = string.Empty
};
prescriber.MobileNumber = viewModel.MobileNumber;
prescriber.PhoneNumber = viewModel.PhoneNumber;
prescriber.DateOfBirth = viewModel.DateOfBirth;
prescriber.AHPRANumber = viewModel.AhpraNumber;
prescriber.ClinicName = viewModel.ClinicName;
prescriber.ClinicWebUrl = viewModel.ClinicWebUrl;
prescriber.Qualifications = viewModel.Qualifications;
prescriber.JobTitle = viewModel.JobTitle;
UserRepository.Commit(prescriber);
}
return View(viewModel);
}
The above code will save a new prescriber (tested by uncommenting out the commented out line etc).
I am using NHProf and have confirmed that no sql is sent to the database for the Update. I can see the read being performed but that's it.
It seems to me that NHibernate doesn't recognise the entity as being changed and therefore does not generate the sql. Or possibly the transaction isn't being committed?
I've been scouring the webs for a few hours now trying to work this one out and as a last act of desperation have posted on SO. Any ideas? :)
Oh and in NHProf I see three Sessions (1 for the GetPrescriber call from the repo, one I assume for the update (with no sql) - and one for some action in my actionfilter on the base class). I also get an alert about the use of implicit transactions. This confuses me because I thought I was doing everything I needed to get an transaction - using AutoTx and the Transaction attribute. I also expected there to be only 1 session per webrequest, as per my Windsor config.
UPDATE: It seems, after spending the day reading through the source for NHibernateFacility and AutoTx Facility for automatic transactions, that AutoTx is not setting the Interceptors on my implementation of INHibernateInstaller. It seems this means whenever SessionManager calls OpenSession it is calling the default version with no parameter, rather than the one that accepts an Interceptor. Internally AutoTxFacility registers TransactionInterceptor with windsor, so that it can be added the Interceptor on my INHibernateInstaller concrete, by windsor making use of the AutoTx's TransactionalComponentInspector
AutoTxFacility source on github
To me it looks like creating sessions for every call to the repository. A session should span the whole business operation. It should be opened at the beginning and committed and disposed at the end.
There are other strange things in this code.
Commit is a completely different concept than SaveOrUpdate.
And you don't need to tell NH to store changes anyway. You don't need to call session.Save for objects that are already in the session. They are stored anyway. You only need to call session.Save when you add new objects.
Make sure that you use a transaction for the whole business operation.
There is one most likely "unintended" part in the code snippet above. And proven by observation made by NHProf
Oh and in NHProf I see three Sessions (1 for the GetPrescriber call
from the repo, one I assume for the update (with no sql) - and one for
some action in my actionfilter on the base class).
Calling the OpenSession() is triggering creation of a new session instances.
protected ISession Session
{
get { return _sessionManager.OpenSession(); }
}
So, whenever the code is accessing the Session property, behind is new session instance created (again and again). One session for get, one for udpate, one for filter...
As we can see here, the session returned by SessionManager.OpenSession() must be used for the whole scope (Unit of work, web request...)
http://docs.castleproject.org/Windsor.NHibernate-Facility.ashx
The syntaxh which we need, si to create one session (when firstly accessed) and reuse it until enf of scope (then later correctly close it, commit or rollback transaction...). Anyhow, first thing right now is to change the Session property this way:
ISession _session;
protected ISession Session
{
get
{
if (_session == null)
{
_session = sessionFactory.OpenSession();
}
return _session;
}
}
After spending a full day yesterday searching through the AutoTx and NHibernate facilities on github and getting nowhere, I started a clean project in an attempt to replicate the problem. Unfortunately for the replication, everything worked! I ran Update-Package on my source and brought down new version of Castle.Transactions and I was running correctly. I did make a small adjustment to my own code. That was to remove the UserRepository.Commit line.
I did not need to modify how I opened sessions. That was taken care of by the SessionManager instance. With the update to Castle.Transactions, the Transaction attribute is being recognised and a transaction is being created (as evidenced by no more alert in NHProf).

Raven DB: How can I delete all documents of a given type

More specifically in Raven DB, I want to create a generic method with a signature like;
public void Clear<T>() {...
Then have Raven DB clear all documents of the given type.
I understand from other posts by Ayende to similar questions that you'd need an index in place to do this as a batch.
I think this would involve creating an index that maps each document type - this seems like a lot of work.
Does anyone know an efficient way of creating a method like the above that will do a set delete directly in the database?
I assume you want to do this from the .NET client. If so, use the standard DocumentsByEntityName index:
var indexQuery = new IndexQuery { Query = "Tag:" + collectionName };
session.Advanced.DocumentStore.DatabaseCommands.DeleteByIndex(
"Raven/DocumentsByEntityName",
indexQuery,
new BulkOperationOptions { AllowStale = true });
var hilo = session.Advanced.DocumentStore.DatabaseCommands.Get("Raven/H‌​ilo/", collectionName);
if (hilo != null) {
session.Advanced.DocumentStore.DatabaseCommands.Delete(hilo.‌​Key, hilo.Etag);
}
Where collectionName is the actual name of your collection.
The first operation deletes the items. The second deletes the HiLo file.
Also check out the official documentation - How to delete or update documents using index.
After much experimentation I found the answer to be quite simple, although far from obvious;
public void Clear<T>()
{
session.Advanced.DocumentStore.DatabaseCommands.PutIndex(indexName, new IndexDefinitionBuilder<T>
{
Map = documents => documents.Select(entity => new {})
});
session.Advanced.DatabaseCommands.DeleteByIndex(indexName, new IndexQuery());
}
Of course you almost certainly wouldn't define your index and do your delete in one go, I've put this as a single method for the sake of brevity.
My own implementation defines the indexes on application start as recommended by the documentation.
If you wanted to use this approach to actually index a property of T then you would need to constrain T. For example if I have an IEntity that all my document classes inherit from and this class specifies a property Id. Then a 'where T : IEntity' would allow you to use that property in the index.
It's been said in other places, but it's also worth noting that once you define a static index Raven will probably use it, this can cause your queries to seemingly not return data that you've inserted:
RavenDB Saving to disk query
I had this problem as well and this is the solution that worked for me. I'm only working in a test project, so this might be slow for a bigger db, but Ryan's answer didn't work for me.
public static void ClearDocuments<T>(this IDocumentSession session)
{
var objects = session.Query<T>().ToList();
while (objects.Any())
{
foreach (var obj in objects)
{
session.Delete(obj);
}
session.SaveChanges();
objects = session.Query<T>().ToList();
}
}
You can do that using:
http://blog.orangelightning.co.uk/?p=105

NHibernate second-level caching - evicting regions

We have a number of cache regions set up in our nHibernate implementation. In order to avoid trouble with load balanced web servers, I want to effectively disable the caching on the pages that edit the cached data. I can write a method that clears out all my query caches, my class caches and my entity caches easily enough.
But what I really want is to clear the cache by region. sessionFactory.EvictQueries() will take a region parameter, but Evict() and EvictCollection() does not. I don't really want to throw away the whole cache here, nor do I want to maintain some sort of clumsy dictionary associating types with their cache regions. Does nHibernate have a way to ask an entity or collection what its caching settings are?
thanks
I've just done the same thing. For everyone's benefit, here is the method I constructed:
public void ClearCache(string regionName)
{
// Use your favourite IOC to get to the session factory
var sessionFactory = ObjectFactory.GetInstance<ISessionFactory>();
sessionFactory.EvictQueries(regionName);
foreach (var collectionMetaData in sessionFactory.GetAllCollectionMetadata().Values)
{
var collectionPersister = collectionMetaData as NHibernate.Persister.Collection.ICollectionPersister;
if (collectionPersister != null)
{
if ((collectionPersister.Cache != null) && (collectionPersister.Cache.RegionName == regionName))
{
sessionFactory.EvictCollection(collectionPersister.Role);
}
}
}
foreach (var classMetaData in sessionFactory.GetAllClassMetadata().Values)
{
var entityPersister = classMetaData as NHibernate.Persister.Entity.IEntityPersister;
if (entityPersister != null)
{
if ((entityPersister.Cache != null) && (entityPersister.Cache.RegionName == regionName))
{
sessionFactory.EvictEntity(entityPersister.EntityName);
}
}
}
}
OK, looks like i've answered my own question. The default interface that's returned when you pull out the nHibernate metadata doesn't provide information on caching, however if you dig around in the implementations of it, it does. A bit clumsy, but it does the job.

With NHibernate, how can I create an INHibernateProxy?

After lots of reading about serialization, I've decided to try to create DTOs. After more reading, I decided to use AutoMapper.
What I would like to do is transform the parent (easy enough) and transform the entity properties if they've been initialized, which I've done with ValueResolvers like below (I may try to make it generic once I get it fully working). This part works.
public class OrderItemResolver : ValueResolver<Order, OrderItem>
{
protected override OrderItem ResolveCore(Order source)
{
// could also use NHibernateUtil.IsInitialized(source.OrderItem)
if (source.OrderItem is NHibernate.Proxy.INHibernateProxy)
return null;
else
return source.OrderItem;
}
}
}
When I transform the DTO back to an entity, for the entities that weren't initialized, I want to create a proxy so that if the entity wants to access it, it can. However, I can't figure out how to create a proxy. I'm using Castle if that's relevant.
I've tried a bunch of things with no luck. The below code is a mess, mainly because I've been trying things at random without knowing what I should be doing. Anybody have any suggestions?
public class OrderItemDTOResolver : ValueResolver<OrderDTO, OrderItem>
{
protected override OrderItem ResolveCore(OrderDTO source)
{
if (source.OrderItem == null)
{
//OrderItem OrderItem = new ProxyGenerator().CreateClassProxy<OrderItem>(); // Castle.Core.Interceptor.
//OrderItem OrderItem = new ProxyGenerator().CreateClassProxy<OrderItem>();
//OrderItem.Id = source.OrderItemId;
//OrderItem OrderItem = new OrderItem();
//var proxy = new OrderItem() as INHibernateProxy;
//var proxy = OrderItem as INHibernateProxy;
//return (OrderItem)proxy.HibernateLazyInitializer
//ILazyInitializer proxy = new LazyInitializer("OrderItem", OrderItem, source.OrderItemId, null, null, null, null);
//return (OrderItem)proxy;
//return (OrderItem)proxy.HibernateLazyInitializer.GetImplementation();
//return OrderItem;
IProxyTargetAccessor proxy = new Castle.Core.Interceptor.
var initializer = new LazyInitializer("OrderItem", typeof(OrderItem), source.OrderItemId, null, null, null, null);
//var proxyFactory = new SerializableProxyFactory{Interfaces = Interfaces, TargetSource = initializer, ProxyTargetType = IsClassProxy};
//proxyFactory.AddAdvice(initializer);
//object proxyInstance = proxyFactory.GetProxy();
//return (INHibernateProxy) proxyInstance;
return null;
//OrderItem.Id = source.OrderItemId;
//return OrderItem;
}
else
return OrderItemDTO.Unmap(source.OrderItem);
}
}
Thanks,
Eric
Maybe I over complicated it. This seems to work. Anybody see any issues with it?
public class OrderItemDTOResolver : ValueResolver<OrderDTO, OrderItem>
{
protected override OrderItem ResolveCore(OrderDTO source)
{
if (source.OrderItem == null)
return NHibernateSessionManager.Instance.Session.GetISession().Load<OrderItem>(source.AgencyId);
else
return OrderItemDTO.Unmap(source.OrderItem);
}
}
This may be one of those cases where the answer is "don't", or at least "you probably shouldn't". If you're mapping DTOs into NHibernate mapped objects directly you're not really using the mapped objects as domain objects, just as a fancy way to push data in and out of the database. This of course may be all you're after but having done this myself in the past I've found that it's problematic trying to use the same DTO data format in both directions. If you're going cross-process you've turned the service into a (difficult to maintain) CRUD layer. If you're in the same process you're doing unnecessary data shuffling with DTOs.
Sending DTOs out is fine, but consider projecting the data into a format more closely aligned with what the client actually needs. What you get back is better expressed in specific DTOs that express only the data needed to perform the actual action (Command objects, essentially). With a few automatic properties they're trivial to construct. You can then have a business method that performs the necessary action with only the necessary information, and that in a format suited to the action being performed. My primary use of AutoMapper (which does rock) these days is to translate incoming DTOs into types that domain methods can consume.
Also, public setters on mapped objects are undesirable because they allow the object to be manipulated by any code without any validation. This means any modification to them can leave them in an invalid state.
If you don't really care about the above (and it's not always applicable) the way you load individual instances does leave you open do doing many individual database loads which is a potential performance issue.

Get existing entity if it exists or create a new one

I'm importing data that may or may not exist already in my database. I'd like NHibernate to associate any entities with the existing db one if it exists (probably just setting the primary key/id), or create a new one if it doesn't. I'm using S#arp architecture for my framework (MVC 2, NHibernate, Fluent).
I've added the [HasUniqueDomainSignature] attribute to the class, and a [DomainSignature] attribute to the properties I want to use for comparison. The only way I can think to do it (which is not an acceptable solution and may not even work) is the following (psuedo C#):
foreach (Book importedBook in importedBooks){
foreach (Author author in importedBook.Authors){
if (!author.IsValid()){ // NHibernate Validator will check DomainSignatures
author = _authorRepository.GetByExample(author); // This would be to get the db object with the same signature,
//but I don't think I could even update this as I iterate through it.
}
}
}
As you can see, this is both messy, and non-sensical. Add to that the fact that I've got a half dozen associations on the Book (subject, format, etc), and it doesn't make any sense. There's got to be an easy way to do this that I'm missing. I'm not a novice with NHibernate, but I'm definitely not an expert.
I might not be understanding the problem, but how can the data "may or may not exist in the database"? For example, if a Book has 2 Authors, how is the relationship stored at the database level if the Author doesn't exist?
It seems as if you're trying to use NHibernate to import your data (or create an entity if it doesn't exist) which doesn't seem correct.
Most database implementations support a conditional UPDATE-or-INSERT syntax. Oracle, for example, has a MERGE command. In combination with a Hibernate <sql-insert> block in your mapping you should be able to work something out. I don't know Fluent but I assume it supports this too.
Just realize I never gave an answer or approved another's answer. I ended up just writing a new SaveOrUpdate which takes a parameter to check for existing before persisting. I also added an attribute to my domain models to overwrite when saving/updating (although in retrospect it's only on updating that it'd be overwriting).
Here's the code if it can help anyone else in this dilemma:
public TEntity SaveOrUpdate<TEntity>(TEntity entity, bool checkForExistingEntity)
{
IRepository<TEntity> repository = new Repository<TEntity>();
if (checkForExistingEntity) {
if (entity is Entity) {
IEnumerable<PropertyInfo> props = (entity as Entity).GetSignatureProperties();
Dictionary<string, object> parameters =
props.ToDictionary(propertyInfo => propertyInfo.Name, propertyInfo => propertyInfo.GetValue(entity, null));
TEntity duplicateEntity = repository.FindOne(parameters);
if (duplicateEntity != null) {
// Update any properties with the OverwriteOnSaveUpdate attribute
foreach (var property in RepositoryHelper.GetUpdatableProperties(typeof(TEntity)))
{
object initialValue = property.GetValue(entity, null);
property.SetValue(duplicateEntity, initialValue, null);
}
// Fill in any blank properties on db version
foreach (var property in typeof(TEntity).GetProperties())
{
if (property.GetValue(duplicateEntity, null) == null) {
object initialValue = property.GetValue(entity, null);
property.SetValue(duplicateEntity, initialValue, null);
}
}
return duplicateEntity;
}
}
}
return SaveOrUpdate(entity);
}