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();
}
}