They say that to build a session factory in NHibernate is expensive and that it should only happen once. I use a singleton approach on this. This is done on the first time that a session is requested.
My question : Would there every be a time when you should close the Session factory? If so, when would one do this?
This is what i do in Java with Hibernate :
public class HibernateUtil
{
private static final SessionFactory sessionFactory;
static
{
try
{
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex)
{
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory()
{
return sessionFactory;
}
}
You can free your SessionFactory when you don't need it anymore I guess, but honestly I've never closed my session factory
To AZ: This is referring to the SessionFactory, not the session. Though I wouldn't say there should only be one instance of SessionFactory. There should be one per unique configuration. For instance, if a single app is connecting to 2 different databases, then you need 2 different SessionFactory instances.
No, it dies with your program and that is fine.
There should be only one instance of sessionfactory . The code above is not an example of singleton pattern.
If you are working in an enterprise application, I would suggest use Spring with hibernate .
That would help you out in managing transactions and take away all this headache of managing session factory
Related
tl;dr How can I use Entity Framework in a multithreaded .NET Core API application even though DbContext is not threadsafe?
Context
I am working on a .NET Core API app exposing several RESTful interfaces that access the database and read data from it, while at the same time running several TimedHostedServices as background working threads that poll data regularly from other webservices and store them into the database.
I am aware of the fact that DbContext is not threadsafe. I read a lot of docs, blog Posts and answers here on Stackoverflow, and I could find a lot of (partly contradictory) answers for this but no real "best practice" when also working with DI.
Things I tried
Using the default ServiceLifetime.Scoped via the AddDbContext extension method results in exceptions due to race conditions.
I don't want to work with locks (e.g. Semaphore), as the obvious downsides are:
the code is polluted with locks and try/catch/finally for safely releasing the locks
it doesn't really seem 'robust', i.e. when I forget to lock a region that accesses the DbContext.
it seems redundant and 'unnatural' to artificially syncronize db access in the app when working with a database that also handles concurrent connections and access
Not injecting MyDbContext but DbContextOptions<MyDbContext> instead, building the context only when I need to access the db, using a using statement to immediatelly dispose it after the read/write seems like a lot of resource usage overhead and unnecessarily many connection opening/closings.
Question
I am really puzzled: how can this be achived?
I don't think my usecase is super special - populating the db from a Background worker and querying it from the web API layer - so there should be a meaningful way of doing this with ef core.
Thanks a lot!
You should create a scope whenever your TimedHostedServices triggers.
Inject the service provider in your constructor:
public MyServiceService(IServiceProvider services)
{
_services = services;
}
and then create a scope whenever the task triggers
using (var scope = _services.CreateScope())
{
var anotherService = scope.ServiceProvider.GetRequiredService<AnotherService>();
anotherService.Something();
}
A more complete example is available in the doc
Another approach to create own DbContextFactory and instantiate new instance for every query.
public class DbContextFactory
{
public YourDbContext Create()
{
var options = new DbContextOptionsBuilder<YourDbContext>()
.UseSqlServer(_connectionString)
.Options;
return new YourDbContext(options);
}
}
Usage
public class Service
{
private readonly DbContextFactory _dbContextFactory;
public Service(DbContextFactory dbContextFactory)
=> _dbContextFactory = dbContextFactory;
public void Execute()
{
using (var context = _dbContextFactory.Create())
{
// use context
}
}
}
With factory you don't need to worry about scopes anymore, and make your code free of ASP.NET Core dependencies.
You will be able to execute queries asynchronously, which not possible with scoped DbContext without workarounds.
You always be confident about what data saved when calling .SaveChanges(), where with scoped DbContext there are possibilities that some entity were changed in other class.
In a couple of .NET C# webservice projects that i have done i have made access to db static with help of the singleton pattern. Then the other day my friend told me that this is a bad thing to do, because if a lot of request is made for the same db entity then the db would be locked because of the static instance. Is my friends assumptations right? I thought that every new request would make a new instance of the class?
The implementation of the singleton class looks like this:
public class WebService
{
private readonly IFactory _factory;
public WebService(IFactory factory)
{
_factory = factory;
}
public IDataRepository Data
{
get
{
return _factory.GetDatabase();
}
}
}
public static class WebServiceImpl
{
private static readonly WebService _webService = new WebShop(new WebserviceFactoryImpl());
public static WebService webService { get { return _webService; } }
}
_factory.GetDatabase() returns a new instace of the Database class.
Looking at WebServiceImpl, all calls will be sharing a single WebService instance. Now, this isn't necessarily a problem, depending on how that is implemented; for example, if _factory.GetDatabase(); ends up getting called per-request, then it might be that you are getting away with it. Depending further on what GetDatabase() does - i.e. does it get a new instance per call? or does it give you the same instance every time? Simply: we don't have enough information there to answer fully. But:
sharing a single database connection between requests is dangerous; either you need to lock / synchronize, or you risk lots of errors (database connections are not usually written to be thread-safe)
sharing an ORM between requests is even worse: in addition to everything above, you also get issues with data accumulating in the identity / object cache; ORM instances (data-context, etc) are intended to be short-lived and then discarded (and sometimes: disposed)
Having static access to the database is not necessarily a problem; it all comes down to how that is implemented - for example, a static-based API could still create (and dispose) a connection on every call.
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/
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.
Im having issues configuring application using windsor, facilities and nhibernate.
Im getting this exception:
ObjectDisposedException: Session is closed
Shouldnt windsor take care of instantiating session per request and opening it when I have configuration like this? Could I miss some configuration?
Here is my confuguration:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Configuration config = BuildDatabaseConfiguration();
Kernel.Register(
Component.For<ISessionFactory>()
.LifeStyle.Singleton
.UsingFactoryMethod(config.BuildSessionFactory),
Component.For<ISession>()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession()));
}
private Configuration BuildDatabaseConfiguration()
{
return Fluently.Configure()
.Database(SetupDatabase)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<RnUlice>())
.ExposeConfiguration(ConfigurePersistence)
.BuildConfiguration() ;
}
......
}
If your Repository<T> gets a ISession in its constructor and it's singleton (default lifestyle), then it will only work in the first request you call your repository. In subsequent requests the repository will still have the same ISession as in the first call (because repository is singleton), but that session is now closed and invalid to use, therefore the error you see.
This is why most of the time you don't want a singleton depending on other components with "shorter" lifestyles (like per-web-request or transient).
See this article for a more thorough analysis of common lifestyle issues.
I figured out what was wrong. I forgot to configure my repository lifestyle to Transient. I dont quite understand how this is a problem though.
container.Register(Component.For(typeof(IRepository<>))
.ImplementedBy(typeof(Repository<>)).LifeStyle.Transient);
I wonder what is the default lifestyle then? I was reading in docs that it is singleton?! How could that be a problem?