Different behaviour from .Use(Func<T>) and .Use(Func<IContext, Func<T>>) when using custom lifecycle

40 views
Skip to first unread message

Peter McEvoy

unread,
May 4, 2012, 8:49:41 AM5/4/12
to structure...@googlegroups.com
I am using a custom lifecycle to enable a "multi tenant" style web application.  In particular, I want certain objects to be associated with a particular URL Domain, where a domain is a function of an incoming HttpRequest.  Thus, I am able to make a declaration like this:

For<ISupportedLocalizations>()
.LifecycleIs(new DomainLifecycle())
.ConditionallyUse(c =>
{
c.TheDefault.IsThis(
new SupportedLocalizations(
CultureInfo.GetCultureInfo("es-ES"),
new List<CultureInfo> { CultureInfo.GetCultureInfo("en-US"), CultureInfo.GetCultureInfo("ro-RO") })
);

c.If(RequestIsForDomain("www.xxx.yyy")).ThenIt.IsThis(
new SupportedLocalizations(
      CultureInfo.GetCultureInfo("en-US"),
       new List<CultureInfo> {CultureInfo.GetCultureInfo("ro-RO")})
);
});

Now, there are situations (application startup) where I need objects to be resolved by structuremap, but I do not have access to the domain, because there is no RequestContext.  In these cases, I usually inject a factory method that will have access to Request Context at runtime:

For<Func<ISupportedLocalizations>>()
.Singleton()
.Use(() => ObjectFactory.GetInstance<ISupportedLocalizations>());


I don't really like this syntax, because everywhere I am reading that one should not use ObjectFactory.GetInstance.  So I figured out that the following _should_ be equivalent:

For<Func<ISupportedLocalizations>>()
.Singleton()
.Use(ctx => () => ctx.GetInstance<ISupportedLocalizations>());

In the first declaration, I am seeing the expected behaviour  - namely the factory method is getting called and the ISuportLocaliztions instance that gets resolved is appropriate to the incoming request.   

However, if I use the second version, I am NOT seeing that behaviour - I get the FIRST ISupportedLocalizations instance that was created.

I'm baffled and will need to stick with the first syntax until I understand a bit more.

I know I will probably be asked for the definition of DomainLifecycle so here it is:

public class DomainLifecycle : ILifecycle
{
private static readonly ConcurrentDictionary<string, MainObjectCache> _tenantCaches = new ConcurrentDictionary<string, MainObjectCache>();

public IObjectCache FindCache()
{
var domain = GetDomainLower();
var cache = _tenantCaches.GetOrAdd(domain, new MainObjectCache());
return cache;
}

public void EjectAll()
{
FindCache().DisposeAndClear();
}
public string Scope
{
get { return "Domain"; }
}
private static string GetDomainLower()
{
var requestDomain = HttpContext.Current.Request.Headers["host"];

if (!string.IsNullOrEmpty(requestDomain))
{
var indexOfPortSeparator = requestDomain.IndexOf(":", StringComparison.Ordinal);
if (indexOfPortSeparator > 0)
requestDomain = requestDomain.Substring(0, indexOfPortSeparator);
}
else
//HTTP spec says that HOST header MUST be specified, we are unlikely to get to this line (maybe in an incorrect Unit test tho)
requestDomain = HttpContext.Current.Request.Url.Host;

return requestDomain.ToLower();
}
}

Reply all
Reply to author
Forward
0 new messages