Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The general answer is "Whoever instantiates the <code>ISession</code> should dispose of it. If the transaction has not been committed, this is effectively a rollback."</p> <p>I've had success by using the command pattern to define an operation that I want to perform on a unit of work. Say we have a <code>Person</code> entity and one of the things we can do is change a person's name. Let's start with the entity:</p> <pre><code>public class Person { public virtual int Id { get; private set; } public virtual string Name { get; private set; } public virtual void ChangeName(string newName) { if (string.IsNullOrWhiteSpace(newName)) { throw new DomainException("Name cannot be empty"); } if (newName.Length &gt; 20) { throw new DomainException("Name cannot exceed 20 characters"); } this.Name = newName; } } </code></pre> <p>Define a simple POCO Command like this:</p> <pre><code>public class ChangeNameCommand : IDomainCommand { public ChangeNameCommand(int personId, string newName) { this.PersonId = personId; this.NewName = newName; } public int PersonId { get; set; } public string NewName { get; set; } } </code></pre> <p>...and a Handler for the command:</p> <pre><code>public class ChangeNameCommandHandler : IHandle&lt;ChangeNameCommand&gt; { ISession session; public ChangeNameCommandHandler(ISession session) { // You could demand an IPersonRepository instead of using the session directly. this.session = session; } public void Handle(ChangeNameCommand command) { var person = session.Load&lt;Person&gt;(command.PersonId); person.ChangeName(command.NewName); } } </code></pre> <p>The goal is that code that exists outside of a Session/Work scope can do something like this:</p> <pre><code>public class SomeClass { ICommandInvoker invoker; public SomeClass(ICommandInvoker invoker) { this.invoker = invoker; } public void DoSomething() { var command = new ChangeNameCommand(1, "asdf"); invoker.Invoke(command); } } </code></pre> <p>The invocation of the command implies "do this command on a unit of work." This is what we want to happen when we invoke the command:</p> <ol> <li>Begin an IoC nested scope (the "Unit of Work" scope)</li> <li>Start an ISession and Transaction (this is probably implied as part of step 3)</li> <li>Resolve an <code>IHandle&lt;ChangeNameCommand&gt;</code> from the IoC scope</li> <li>Pass the command to the handler (the domain does its work)</li> <li>Commit the transaction</li> <li>End the IoC scope (the Unit of Work)</li> </ol> <p>So here's an example using <a href="http://code.google.com/p/autofac/" rel="nofollow">Autofac</a> as the IoC container:</p> <pre><code>public class UnitOfWorkInvoker : ICommandInvoker { Autofac.ILifetimeScope scope; public UnitOfWorkInvoker(Autofac.ILifetimeScope scope) { this.scope = scope; } public void Invoke&lt;TCommand&gt;(TCommand command) where TCommand : IDomainCommand { using (var workScope = scope.BeginLifetimeScope("UnitOfWork")) // step 1 { var handler = workScope.Resolve&lt;IHandle&lt;TCommand&gt;&gt;(); // step 3 (implies step 2) handler.Handle(command); // step 4 var session = workScope.Resolve&lt;NHibernate.ISession&gt;(); session.Transaction.Commit(); // step 5 } // step 6 - When the "workScope" is disposed, Autofac will dispose the ISession. // If an exception was thrown before the commit, the transaction is rolled back. } } </code></pre> <p>Note: The <code>UnitOfWorkInvoker</code> I've shown here is violating <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle" rel="nofollow">SRP</a> - it is a <code>UnitOfWorkFactory</code>, a <code>UnitOfWork</code>, and an <code>Invoker</code> all in one. In my actual implementation, I broke them out.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload