I currently have this Fluent NHibernate configuration:
public class NHibernateConfig
{
public static Configuration Configure()
{
var cfg = Fluently.Configure()
.Database(Config.PersistenceConfiguration)
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<SomeAssembly>();
m.FluentMappings.Conventions.AddFromAssemblyOf<EnumConvention>(); })
.ExposeConfiguration(x => x.SetProperty("current_session_context_class", "thread_static"))
.BuildConfiguration();
return cfg;
}
}
My question is about the exposed property "current_session_context_class." I know of two values for this: thread_static or web. A colleague of mine pointed out another value, which is call. Are there any known documentation for values of this property? If not, are there any good descriptions for these values? I've scoured Google for hours for some explanations with no valid result.
Here is my attempt to explain these (Any additional input would be welcome):
Difference between CallSessionContext, ThreadLocalSessionContext and ThreadStaticSessionContext
There is a section on these in the standard NH documentation but I don't think they do a very good job explaining it or giving any examples on how exactly to use it. Here is the NH documentation link.
http://nhibernate.info/doc/nhibernate-reference/architecture.html#architecture-current-session
There are several decent articles on StackOverflow about how you would use this:
What is the best NHibernate session management approach for using in a multithread windows service application?
NHibernate.HibernateException: No session bound to the current context
"managed_web", "call", "thread_static", and "web" are possible values. Configured like this in nhibernate configuration:
<property name="current_session_context_class">call</property>
Once this is configured, you can use SessionFactory.GetCurrentSession(). You have to bind and unbind session yourself. One sample implementation:
if (CallSessionContext.HasBind(_sessionFactory))
{
session = SessionFactory.GetCurrentSession();
}
else
{
session = SessionFactory.OpenSession();
CallSessionContext.Bind(session);
}
Instead of CallSessionContext, you can also use ManagedWebSessionContext or ThreadStaticSessionContext.
ManagedWebSessionContext - Suitable for Asp.Net application. Session is attached to current HttpContext (supplied as parameter while binding).
ManagedWebSessionContext.Bind(HttpContext.Current,session)
ThreadStaticSessionContext - Session is attached to current thread (I wont encourage using this as threads keep switching abruptly and your attached session could be lost).
CallSessionContext - Suitable for non-web applications. Session is attached to CallContext. I could be wrong but I imagine this as session attached to SessionFactory itself. As long as you have one SessionFactory for entire application, this approach will ensure you will never get concurrent active sessions.
Related
I have a query, IGetHamburgers, that calls an external API.
I've registered the implementation of IGetHamburgers in my DI container as a Singleton. Im using Polly as a Circuitbreaker, if two requests fails the circuit will open.
My goal is that all calls to the Hamburger api should go through the same circuitbreaker, if GetHamburgers fails, then all other calls should fail as well.
How should I use my Policy? Should I register my Policy as a field like this:
private Policy _policy;
private Policy Policy
{
get
{
if(this_policy != null)
{
return this_policy;
}
this._policy = Policy
.Handle<Exception>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));
return this._policy;
}
}
public object Execute(.......)
{
return Policy.Execute(() => this.hamburgerQuery.GetHamburgers());
}
OR
public object Execute(.......)
{
var breaker = Policy
.Handle<Exception>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));
return breaker.Execute(() => this.hamburgerQuery.GetHamburgers());
}
I guess that the first option is the correct way since then the Policy object will always be the same and can keep track of the exception count and stuff like that.
My question is, will option number two work as well? I've found a lot of samples/examples on Pollys Github but I can't find any "real world" examples where Polly is used together with DI and stuff like that?
I guess that the first option is the correct way since then the Policy object will always be the same and can keep track of the exception count and stuff like that.
Correct. This is described in the Polly wiki here. In brief:
Share the same breaker policy instance across call sites when you want those call sites to break in common - for instance they have a common downstream dependency.
Don't share a breaker instance across call sites when you want those call sites to have independent circuit state and break independently.
See this stackoverflow answer for a more extensive discussion of configuring policies separately from their usage, injecting them to usage sites by DI, and the effects of re-using the same instance (for example a singleton) versus using separate instances, across the full range (at June 2017) of Polly policies.
will option number two work as well?
No (for the converse reason: each call creates a separate instance, so won't share circuit statistics/states with other calls).
Context:
I am using DI in my Web application. (I am using NInject, but hopefully this should not matter)
Some places constructor injection is not possible, for example in my custom log4net database logger (that's not me, who instantiates my custom logger instead the log4net framework). So I am using my DI container there in service locator DP mode, and asking an instance resolve explicitly in the logger code.
Note this is just a sample, in many other cases I had to use NInject as service locator DP instead of constructor injection.
Now the problem:
I have an IAuditContextProvider which serves current request's audit data, like IP etc. The question arises how I configure my DI container to instantiate a concrete provider. So far I've used a request scope (singleton by request) what is supported out of box by NInject.
However recently I faced the fact I had to start a background processing initiated by a request. This is done by
// This is 'outside' it's actually a request serving method running in the request context, btw it is an MVC action method,
// Pseudo code:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// I would like to get the very same resolved auditProvider instance here as outside.
// Please note: outer local variables are not solution, because of implicit calls here inside, for example if there is a logging statement here, then the service locator in the custom logger must resolve the very same instance as outside
// Some how I must 'stamp' this thread to be the 'same' as the outside
// request thread in point of view of my custom scope resolver (see below)
}
Note: Configuring the DI container a wide scoped singleton are not solution because of multiple requests are server parallel, and they can not use a common auditProvider.
OK, I thought this is what for custom (resolving) scopes are for. Here is the pseudo code how I am configuring my DI container:
kernel
.Bind(typeof(IAuditContextProvider))
.To(typeof(WebAuditContextProvider)).InScope(dummy =>
{
// Here I have to return a very same object/number/id when in
// 'outside' the request thread, and inside the worker thread.
// This way I hopefully get the very same instance when resolving.
// To be short: I have no idea how?
});
I don't think there is a good answer for your question within the current bounds.
I do have an alternative suggestion - just perform the work synchronously in another process. This would require a form of inter-process communication (IPC) but shouldn't be too difficult.
A simple but effective form of IPC is just writing a record to a database table (acting like a queue) and having a windows service/daemon polling for new records to "process". In this example, you would put a record in the table with the contextual information (user id, etc) and the service would utilize this context to perform the work synchronously, but the workflow would be asynchronous to the Web UI.
This also has a nice side benefit: You can start to build monitoring, retry logic, etc into the service. These things are much harder to do reliably within an ASP.NET model.
You could forgo the database queue completely by using something like message queues/buses/events, but the basic concept is the same.
Update:
Did you try to use closures in C#? Like this:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// with closure you'll get that very variable you need:
auditProvider.SomeMethod();
}
You should read whole article about closures by John Skeet and how they can help you together with TPL.
Other useful information:
Such DI is being called as Ambient Context in famous book Dependency Injection by M. Seeman:
A truly universal CROSS-CUTTING CONCERN can potentially pollute a large part of the API for an application if you have to pass an instance around to every collaborator. An alternative is to define a context that’s available to anyone who needs it and that can be ignored by everyone else.
The AMBIENT CONTEXT is available to any consumer via a static property
or method. A consuming class might use it like this:
public string GetMessage() { return SomeContext.Current.SomeValue; }
In this case, the context has a static Current property that a consumer can access. This property may be truly static, or may be associated with the currently executing thread. To be useful in DI scenarios, the context itself must be an ABSTRACTION and it must be possible to modify the context from the outside—in the previous example, this means that the Current property must be writable. The context itself might be implemented as shown in the following listing.
The context is an abstract class, which allows us to replace one context with another implementation at runtime.
public abstract class SomeContext
{
public static SomeContext Current
{
get
{
// Get current context from TLS
var ctx = Thread.GetData(Thread.GetNamedDataSlot("SomeContext")) as SomeContext;
if (ctx == null)
{
ctx = SomeContext.Default;
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), ctx);
}
return ctx;
}
set
{
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), value);
}
}
public static SomeContext Default = new DefaultContext();
public abstract string SomeValue { get; }
}
TLS here stands for Thread Local Storage, which can be useful idea for you here.
Also I suggest you to read about OperationContext class, which can be helpful for you if you want to pass some context for your Task, something like this:
// save current context before task start
var operationContext = OperationContext.Current;
Task.Run(() =>
{
// set current operation context inside your Task with closure
OperationContext.Current = operationContext;
// Your work here
}
We are using Ninject.MVC5 and Ninject.Extention.Conventions in a multi-tenant web environment with multiple databases, one for each tenant along with a primary EF database. When a user logins in, we find them in the primary database and determine what database they should work with so we can bind all our datacontexts to that DB. (We use EF for the primary database and Linq to SQL for the tenant DB).
Here is the initial bind:
private static void RegisterServices(IKernel kernel)
string TennantConnection= ConfigurationManager.AppSettings["DSN"] ?? "";
kernel.Bind<TenantDB>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(string), TennantConnection);
Where TennantConnection is a dummy default connection string initially
Here is the Rebind that is called after the login with the updated connection string
kernel.Rebind<TenantDB>().ToSelf().InRequestScope().WithConstructorArgument(typeof(string), ConfigConnection);
The kernel is injected into the constructor for the rebind class as follows:
public DataContextTennant(IKernel kernel)
All of the rest of the injections are done by convention.
The issue is that when we deploy the site (it happens to be an Azure Cloud app) many of the users get an error of an invalid SQL connection after first login which I believe is due to the rebind. But if they use a private browser session the rebind seems to work both for that session and subsequent sessions.
Although I was unable to resolve it with Ninject, I was able to resolve the issue with Unity. From the controller, after retrieving the tenant connection string, I call this:
public static void RegisterDBTypes(IUnityContainer container, string connection)
{
container.RegisterType<ADataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
container.RegisterType<RDataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
container.RegisterType<PDataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
}
This rebinds the data contexts which flows through to the various services and repositories.
I suggest that Ninject is working as expected. You should look into why the TenantDB is instanciated before login is complete and rebind is done. This should be what's causing your issues.
To do so, you should start with removing your default TenantDB binding:
string TennantConnection= ConfigurationManager.AppSettings["DSN"] ?? "";
kernel.Bind<TenantDB>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(string), TennantConnection);
because after all, this binding only results in a TenantDB which is unusable, so why bother? It's just post-poning the issue to later - making it harder to detect.
It's much better to fail fast - have ninject throw an ActivationException! (which happens if there's no binding).
This should help you in finding out under which circumstances TenantDB is instanciated before login is complete.
Edit: Verification that IBindingRoot.Rebind works:
public class Test
{
[Fact]
public void Foo()
{
const string text1 = "Text1";
const string text2 = "Text2";
var kernel = new StandardKernel();
kernel.Bind<string>().ToConstant(text1);
kernel.Get<string>().Should().Be(text1);
kernel.Rebind<string>().ToConstant(text2);
kernel.Get<string>().Should().Be(text2);
}
}
Ninject's rebind works. I think you're making a configuration mistake and when using ninject, your building parts of your object tree - which depend on the DbContext before the Rebind is done. Thus "pre-login DbContext" leaks through to after login.
If i should be mistaken, you should create a minimal verifiable example and post it on Ninject's Issue tracker.
I may me missing something basic here - but is it possible to retrieve the HttpContext.Current in a custom NLog event?
I am trying to give each request a unique Guid so that I can correlate logging messages to a single event (i.e, tie together each log event for a single request). So, I want to store this Guid in HttpContext.Current.Items, then retrieve it in the NLog target and include it in the log message.
Here is my example target where I'd like to access HttpContext.Current:
[Target("AzureTableTarget")]
public class AzureTableTarget : TargetWithLayout
{
public AzureTableTarget()
{
_appSettings = IoCResolver.Get<IAppSettings>();
}
protected override void Write(LogEventInfo logEvent)
{
var correlationId = HttpContext.Current; //This is always null
var batchOperation = new TableBatchOperation();
CxLogEventBuilder.Build(_appSettings, logEvent).ForEach(batchOperation.Insert);
_loggingTable.ExecuteBatchAsync(batchOperation);
}
}
Nowadays it's easier to retrieve the HTTP Context in a NLog target (works for ASP.NET and ASP.NET Core)
Install NLog.Web (ASP.NET) or NLog.Web.AspNetCore (ASP.NET Core) package
For ASP.NET core, follow the ASP.NET Core - NLog setup
Inherit from AspNetLayoutRendererBase (namespace NLog.Web.LayoutRenderers)
Get the request by calling var context = HttpContextAccessor.HttpContext;
Example:
[LayoutRenderer("aspnet-sessionid")]
[ThreadSafe]
public class AspNetSessionIdLayoutRenderer : AspNetLayoutRendererBase
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
var context = HttpContextAccessor.HttpContext;
var contextSession = context?.Session();
if (contextSession == null)
{
InternalLogger.Debug("HttpContext Session Lookup returned null");
return;
}
builder.Append(contextSession.SessionID); // ASP.NET Core: contextSession.Id
}
}
PS: there are currently many predefined renderers for ASP.NET (Core): https://nlog-project.org/config/?tab=layout-renderers&search=aspnet
If your custom target should capture one (or more) context-specific values, then I recommend that your target inherits from TargetWithContext (or AsyncTaskTarget).
It gives the ability to setup and capture contextproperty-items. Where the Layout can be assigned to capture context-details. Examples of possible context-details easily available from HttpContext:
https://nlog-project.org/config/?tab=layout-renderers&search=package:nlog.web.aspnetcore
For more details about writing custom-targets:
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target-for-structured-logging
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-async-target
Btw. there already exists this custom target that nicely inherits from AsyncTaskTarget:
https://www.nuget.org/packages/NLog.Extensions.AzureCosmosTable/
This article about Working with HttpContext.Current might help. The key, for you, might be that when control passes from one thread to another HttpContext.Current in the new thread can be null.
Here is another question/answer from here on SO that describes HttpContext.Current being null in the context of a web service. The accepted answer suggests turning on ASP.Net compatibility in your web.config file.
I don't know of either of these will help, but they might. I found them by googling for "HttpContext.Current is null", which yielded quite a number of hits. I have done very little ASP.NET development, so I can't really comment on HttpContext.Current from my own personal experience.
Given your use case, I would suggest that you look into System.Diagnostics.CorrelationManager.ActivityId.
One nice feature of ActivityId is that it is "flowed" from parent threads to child threads (including thread pool threads). I think that it works well with Tasks and Parallel operations. Works well meaning that the ActivityId, as set in a parent thread, has the expected value in a child thread.
There is not a LayoutRenderer for ActivityId, but it easy enough to write one. See an example (written against NLog 1.0) here:
Most useful NLog configurations
I'm pretty sure that the "EstimatedBufferSize" stuff is no longer needed, so something like will probably work:
[LayoutRenderer("ActivityId")]
class ActivityIdLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(Trace.CorrelationManager.ActivityId);
}
}
If you go this route, you might consider adding a Format property to the ActivityIdLayoutRenderer to allow you to specify the guid format. See this answer (from me). It contains a lot of useful information about working with guids.
NewGuid vs System.Guid.NewGuid().ToString("D");
See this source file (in NLog's git repository) for an example of how you can implement and use such a Format property:
https://github.com/NLog/NLog/blob/master/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs
I am using NserviceBus 2.5 and was facing the problem NSB caching the Nhibernate Sessions.
I spent sometime on internet and found that class implementing IMessageModule interface is the way to solve this. I also saw the implementation of such at https://github.com/NServiceBus/NServiceBus/blob/v2.5/src/impl/SagaPersisters/NHibernateSagaPersister/NServiceBus.SagaPersisters.NHibernate/NHibernateMessageModule.cs
MyEndPoint is defined like
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomLogging, IWantCustomInitialization
{
public void Init()
{
var location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
var windsorContainer = IoCBootstrapper.InitializeForSession(Path.Combine(location, "MyDll.config")); //This line creates Windsor container without Nhibernate Session I have not written real dll.config name but my code contains
ISessionFactory sessionFactory = MessageSessionFactory.ConfigureSessionFactory();
windsorContainer.Kernel.AddComponentInstance<ISessionFactory>(sessionFactory);
windsorContainer.Register(Component.For(typeof(NHibernateMessageModule)).LifeStyle.Singleton);
windsorContainer.Register(Component.For(typeof(MessageHandler)).LifeStyle.Transient);
NServiceBus.Configure.With(AllAssemblies.Except("XYZ.dll"))
.CastleWindsorBuilder(windsorContainer)
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.LoadMessageHandlers();
SetLoggingLibrary.Log4Net(log4net.Config.XmlConfigurator.Configure);
}
}
I have defined following class to create SessionFactory
public class MessageSessionFactory
{
protected static ISessionFactory sessionFactory;
private static ILog log = LogManager.GetLogger(typeof(MessageSessionFactory));
public static ISessionFactory ConfigureSessionFactory()
{
try
{
if (sessionFactory != null) return sessionFactory;
string connectionString = System.Configuration.ConfigurationManager
.ConnectionStrings["SessionFactoryCS"].ToString();
NHibernate.Cfg.Configuration nHibernateConfiguration =
new NHibernate.Cfg.Configuration();
nHibernateConfiguration.SetProperty(
NHibernate.Cfg.Environment.ProxyFactoryFactoryClass,
typeof(NHibernate.ByteCode.Castle.ProxyFactoryFactory).AssemblyQualifiedName);
nHibernateConfiguration.SetProperty(
NHibernate.Cfg.Environment.Dialect,
typeof(NHibernate.Dialect.MsSql2005Dialect).AssemblyQualifiedName);
nHibernateConfiguration.SetProperty(
NHibernate.Cfg.Environment.ConnectionString, connectionString);
nHibernateConfiguration.SetProperty(
NHibernate.Cfg.Environment.FormatSql, "true");
nHibernateConfiguration.SetProperty(NHibernate.Cfg.Environment.CurrentSessionContextClass,
typeof(NHibernate.Context.ThreadStaticSessionContext).AssemblyQualifiedName);
nHibernateConfiguration.AddAssembly(Assembly.GetCallingAssembly());
sessionFactory = nHibernateConfiguration
.BuildSessionFactory();
return sessionFactory;
}
catch (TypeInitializationException ex)
{
throw new Exception("TO DO :Enter message");
}
}
}
Whenever I try to start the service I see messages like at HandleEndMessage
NHibernate.HibernateException: No current session context configured.
at NHibernate.Context.CurrentSessionContext.GetCurrentSessionContext(ISessionFactory factory)
at NHibernate.Context.CurrentSessionContext.HasBind(ISessionFactory factory)
If I catch the exception here then this error shifts to HandleError
Could you anybody tell me where I could be wrong?
that message means that you haven't configured nhibernate to tell it how to use contextual sessions. the nhibernate contextual sessions feature means that nhibernate will manage keeping track of the current session for you and you only need to worry about binding and unbinding the current session to/from the context and any time you ask the session factory for the current session within that context, you will get the same one. the message module andreas wrote makes use of this feature (and you should too in your handlers if that is how you are managing your sessions - meaning that if you have a dependency in your handler classes on ISessionFactory and get sessions from there, you should use ISessionFactory.GetCurrentSession() instead of ISessionFactory.OpenSesion()).
to fix the problem you are seeing, you need to tell NHibernate how to manage the session context. there are several built in options. the one andreas recommends in his blog post is ThreadStatic. this is fine in your case, as it seems you are only connecting to one database and using one session factory. note that this context class only supports one session factory, so it wouldn't work if you are dealing with more than one. thread static means each thread will have its own session context - you will get the same session as long as you are on the same thread. this works nicely with nservicebus as the handler will execute entirely on a thread and the message module will make sure you are getting a new session with each message and not using the same one from the previous message handled by that thread.
to configure nhibernate for this, you need to set the current_session_context_class property to thread_static. if you are configuring nhibernate directly, you know how you are doing it. if you are using fluent nhibernate, you will need to use the FluentConfiguration.ExposeConfiguration method to do this:
Fluently.Configure()
// whatever else you are doing
.ExposeConfiguration(
c => c.SetProperty("current_session_context_class", "thread_static")
);
here is andreas's post about it:
http://andreasohlund.net/2010/02/03/nhibernate-session-management-in-nservicebus/