Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <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>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. COWho was responsible for calling session.SaveOrUpdate? And who is responsible for calling session.Save or session.Update in case you have assigned Ids? I also don't know if it is best to commit the transaction for each command. In a web application it's common to have a single session and transaction per request (preferably lazily loaded/started) and you commit on the end of the request or rollback if any error occurred. Since the transaction is started by whoever created the session, it's also its responsibility to commit or rollback, or am I wrong?
      singulars
    2. CO@Loudenvier - Depending on the application, a "one transaction per command" pattern may not be appropriate. Commands are just one possible way to delineate transaction boundaries. In my example, the UnitOfWorkInvoker implicitly creates the session and transaction by calling `Resolve`, so it is fine that it commits it. Also, laziness could be added easily (assuming Autofac) by asking the DI container for `Lazy<ISession>`.
      singulars
    3. COI'm too using Lazy<ISession> but with a simpler approach as it is tailor made to web applications. I believe it works pretty much like your own, but it's less sophisticated as I don't require it right now. What I really didn't understand in your sample was where Save or SaveOrUpdate would be called. The ChangeNameCommandHandler only calls person.ChangeName() but it doesn't Save, Update or SaveOrUpdate it... So I was wondering where you keep track of changed/created entities and call the save... Perhaps the command itself should do it, right?
      singulars
 

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