Note that there are some explanatory texts on larger screens.

plurals
  1. POUnity WPF window lifetime manager
    text
    copied!<p>I am using Unity as the DI container in my c# / WPF / NHibernate application.</p> <p>I want to use a custom lifetime manager that will give me a separate NHibernate session per WPF window. The Session returned by the container should be the session for the currently active window.</p> <p>The problem is, I can't get it to work. The container appears to create a new session at each request and then attempt to store this with the lifetime manager, rather than requesting the session from the lifetime manager first.</p> <p>This is the code for my custom lifetime manager:</p> <pre><code>class ActiveWindowLifetimeManager : LifetimeManager { private SynchronizationContext _uiContext; private ICacheFactory _cacheFactory; private ICache&lt;Window, ICache&lt;Guid, object&gt;&gt; _data; private Guid _key; public ActiveWindowLifetimeManager(ICacheFactory cacheFactory, SynchronizationContext uiContext, ICache&lt;Window, ICache&lt;Guid, object&gt;&gt; cache) { _key = Guid.NewGuid(); _uiContext = uiContext; _cacheFactory = cacheFactory; _data = cache; } public override object GetValue() { object result = null; _uiContext.Send(InternalGetValue, result); return result; } public override void SetValue(object newValue) { _uiContext.Send(InternalSetValue, newValue); } public override void RemoveValue() { _uiContext.Send(InternalRemoveValue, null); } // Note - I don't think we need to worry about concurrency issues as everything // is marshalled to the UI thread anyway private void InternalGetValue(object state) { ICache&lt;Guid, object&gt; cache = GetActiveWindowCache(); if (cache != null) { state = cache.Get(_key); } } private void InternalSetValue(object state) { ICache&lt;Guid, object&gt; cache = GetActiveWindowCache(); if (cache != null) { if (cache.Get(_key) == null) { cache.Add(_key, state); } } } private void InternalRemoveValue(object state) { ICache&lt;Guid, object&gt; cache = GetActiveWindowCache(); if (cache != null) { cache.Remove(_key); } } private ICache&lt;Guid, object&gt; GetActiveWindowCache() { ICache&lt;Guid, object&gt; result = null; Window activeWindow = Application.Current.Windows.OfType&lt;Window&gt;().FirstOrDefault(x =&gt; x.IsActive); if (activeWindow == null) { activeWindow = Application.Current.Windows.OfType&lt;Window&gt;().FirstOrDefault(); } if (activeWindow != null) { result = _data.Get(activeWindow); if (result == null) { result = _cacheFactory.GetCache&lt;Guid, object&gt;(100); _data.Add(activeWindow, result); } } return result; } } </code></pre> <p>There are three relevant registrations with the container. The first is the <code>ISession</code>, using the custom lifetime manager. the <code>INHSessionProvider</code> and <code>SessionProviderParameters</code> resolve without any problems.</p> <pre><code>container.RegisterType&lt;ISession&gt;("mainDBSession", container.Resolve&lt;ActiveWindowLifetimeManager&gt;(), new InjectionFactory(c =&gt; { SessionProviderParameters parameters = c.Resolve&lt;SessionProviderParameters&gt;("mainDBSessionProviderParams"); return c.Resolve&lt;INHSessionProvider&gt;(new ParameterOverride("parameters", parameters)).Session; })); </code></pre> <p>The next registers <code>INHUnitOfWork</code>, which is populated with an <code>ISession</code> from the above registration.</p> <pre><code>container.RegisterType&lt;INHUnitOfWork&gt;("mainDBUoW", new InjectionFactory(c =&gt; { return c.Resolve&lt;NHUnitOfWork&gt;(new ParameterOverride("session", c.Resolve&lt;ISession&gt;("mainDBSession"))); })); </code></pre> <p>Finally, the <code>Func&lt;INHUnitOfWork&gt;</code> is the dependency of any actual data access classes.</p> <pre><code>container.RegisterType&lt;Func&lt;INHUnitOfWork&gt;&gt;("mainDBUoWFactory", new InjectionFactory(f =&gt; new Func&lt;INHUnitOfWork&gt;(() =&gt; f.Resolve&lt;INHUnitOfWork&gt;("mainDBUoW")))); </code></pre> <p>I put a break point on my first data access statement and this is what hits <code>ActiveWindowLifetimeManager</code>:</p> <ol> <li>GetValue - lifetime manager key - {ca896ad2-1968-4bc7-94c6-ab2cb0ab08c2} - return null</li> <li>SetValue - lifetime manager key - {ca896ad2-1968-4bc7-94c6-ab2cb0ab08c2} - ISession hash code - 21438532</li> <li>GetValue - lifetime manager key - {ca896ad2-1968-4bc7-94c6-ab2cb0ab08c2} - return ISession hash code - 21438532</li> </ol> <p>At this point, <code>Func&lt;INHUnitOfWork&gt;</code> has returned and the actual session in the supplied <code>INHUnitOfWork</code> has hash code 21438532.</p> <p>I then hit my second data access statement and this is what hits <code>ActiveWindowLifetimeManager</code>:</p> <ol> <li>SetValue - lifetime manager key - {ca896ad2-1968-4bc7-94c6-ab2cb0ab08c2} - ISession hash code - 21320198</li> <li>GetValue - lifetime manager key - {ca896ad2-1968-4bc7-94c6-ab2cb0ab08c2} - return ISession hash code - 21438532</li> </ol> <p>Actual session in the supplied <code>INHUnitOfWork</code> has hash code 21320198 i.e. The container seems to have created a new session first and updated the lifetime manager in 1. The session returned by call 2 appears to be ignored.</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