I am using TransactionScope with nHibernate. For working with nested transactions I have made a helper class along with TransactionScope . My SessionFactory:
public class SessionFactory
{
[ThreadStatic]
private static ISessionFactory iSessionFactory;
[ThreadStatic]
protected static ISession session;
private static object syncRoot = new Object();
private static void buildSessionFactory()
{
lock (syncRoot)
{
if (session == null)
{
if (iSessionFactory == null)
{
Configuration configuration = new Configuration().Configure("hibernate.cfg.xml");
Assembly assembly = Assembly.GetCallingAssembly();
iSessionFactory = configuration.BuildSessionFactory();
}
if (session != null && session.IsOpen)
session.Dispose();
CurrentSessionContext.Bind(iSessionFactory.OpenSession());
session = iSessionFactory.OpenSession();
}
}
}
public static ISession OpenSession
{
get
{
if (session == null || !session.IsOpen)
{
session = null;
buildSessionFactory();
}
return session;
}
}
}
Configuration file is named hibernate.cfg.xml and is:
<configuration>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">
NHibernate.Driver.MySqlDataDriver
</property>
<property name="dialect">NHibernate.Dialect.MySQL55InnoDBDialect</property>
<property name="connection.connection_string">connection string here</property>
<property name="current_session_context_class">thread_static</property>
<property name="show_sql">true</property>
<property name="generate_statistics">true</property>
<mapping assembly="assembly name here"/>
<!-- Here -->
</session-factory>
</hibernate-configuration>
<system.transactions>
<machineSettings maxTimeout="00:00:60" />
</system.transactions>
</configuration>
Helper class for managing nested transactions:
public class TransactionScopeHelper : SessionFactory, iTransactionScopeHelper
{
public static int count = 0;
public TransactionScope getTransactionScope()
{
if (count == 0)
{
if (session != null)
session.Clear();
}
count++;
return new TransactionScope(TransactionScopeOption.Required);
}
public void complete(TransactionScope tx)
{
count--;
if (count == 0)
{
session.Flush();
session = null;
}
tx.Complete();
}
public void rollbackTransaction()
{
count = 0;
session = null;
}
}
Helper class is used only in services and not in repository
An example of usage of helper in service:
public void updateCategory(CategoryDto category_dto)
{
try
{
using (TransactionScope tx = transactionScopeHelper.getTransactionScope())
{
//works here
transactionScopeHelper.complete(tx);
}
}
catch(Exception)
{
transactionScopeHelper.rollbackTransaction();
throw;
}
}
An example of function in repository:
public T getById(long id)
{
ISession session = SessionFactory.OpenSession;
return session.Get<T>(id);
}
Whenever I update some entity , old data is shown after few refreshes and again after some refresh new data is retrieved. This is almost continuous. Why am I getting this problem? Any help would be appreciated.
There is so much "unusual" stuff going on here I barely know where to begin.
SessionFactory
NHibernate's session factory is generelly initialized ONCE on application startup (or when first needed) and then that instance will live for the duration of the program and shared among all threads. We would not typically expect to have a separate instance per thread (as ThreadStatic does). Initializing it repeatedly will be a severe performance penalty.
buildSessionFactory() will dispose the session if there is one - but it won't get to that code unless session is null, so I don't understand what the intention really is here.
At the end of buildSessionFactory(), iSessionFactory.OpenSession() is called twice! Once should be enough.
You are setting current_session_context_class in the configuration file and call CurrentSessionContext.Bind(). So there should be no need for you to maintain your own thread-static session variable. This is what CurrentSessionContext is for.
A session must eventually be disposed. Therefore, calling a method named like OpenSession implies that we get a new session that we are responsible for closing. But your own OpenSession() can return the same session many times, making it difficult to understand who exactly is responsible for closing it. Better to name it CurrentSession or similar.
TransactionScopeHelper
This is also not possible to understand. There is a reference to a variable session, but the definition and setting of this variable is not shown. Code is incomplete. At some points the session reference is set to null but no attempt to dispose it is made, which is mandatory.
Calling Clear() on the session will remove all state from the session and make NHibernate lose track of all loaded objects and therefore be unable to perform dirty check and persist automatically. Other code would need to remember to carefully re-add everything.
Code does not show how and when the session, transaction and transaction scope are actually created. Unable to tell if the interactions are proper. Is the session even aware of the transaction scope?
Finally, to answer your question: "Why am I getting this problem?"
Well, I'm sorry, but the whole situation looks like a mess. And messy code equals messy results. Unless of course the issues I mention above result from partial copy-paste that left out important parts.
Related
I have a web application where many components are registered using .LifestylePerWebRequest(), now I've decided to implement Quartz.NET, a .NET job scheduling library, which executes in separate threads, and not the Request thread.
As such, HttpContext.Current yields null. My services, repositories, and IDbConnection were instanced so far using .LifestylePerWebRequest() because it made it easier to dispose of them when the requests ended.
Now I want to use these components in both scenarios, during web requests I want them to remain unaffected, and in non-request contexts I want them to use a different Lifestyle, I figure I can handle the disposing myself, but how should I go about it for choosing a lifestyle for the components based on the current context?
Currently I register services (for example), like this:
container.Register(
AllTypes
.FromAssemblyContaining<EmailService>()
.Where(t => t.Name.EndsWith("Service"))
.WithService.Select(IoC.SelectByInterfaceConvention)
.LifestylePerWebRequest()
);
I figure I should be using some kind of extension method but I just don't see it..
You should use Hybrid Lifestyle from castleprojectcontrib.
An hybrid lifestyle is one that actually blends two underlying lifestyles: a main lifestyle and a secondary lifestyle. The hybrid lifestyle first tries to use the main lifestyle; if it's unavailable for some reason, it uses the secondary lifestyle. This is commonly used with PerWebRequest as the main lifestyle: if the HTTP context is available, it's used as the scope for the component instance; otherwise the secondary lifestyle is used.
Don't use the same components. In fact, in most scenarios I've seen the "background processing" doesn't even make sense to be in the web process to begin with.
Elaborating based on the comments.
Shoehorning background processing in the web pipeline is compromising your architecture to save a few $ on a EC2 instance. I would strongly suggest to think about this again, but I digress.
My statements still stands, even if you're putting both components in the web process they are two different components used in two different contexts and should be treated as such.
I've had a very similar problem recently - I wanted to be able to run initialisation code based off my container in the Application startup, when HttpContext.Request does not yet exist. I didn't find any way of doing it, so I modified the source of the PerWebRequestLifestyleModule to allow me to do what I wanted. Unfortunately it didn't seem possible to make this change without recompiling Windsor - I was hoping I would be able to do it in an extensible way so I could continue to use the main distribution of Windsor.
Anyway, to make this work, I modified the GetScope function of the PerWebRequestLifestyleModule so that if it was NOT running in a HttpContext (or if HttpContext.Request throws an exception, like it does in Application_Start) then it will look for a Scope started from the container instead. This allows me to use my container in Application_Start using the following code:
using (var scope = container.BeginScope())
{
// LifestylePerWebRequest components will now be scoped to this explicit scope instead
// _container.Resolve<...>()
}
There's no need to worry about explicitly disposing of things, because they will be disposed when the Scope is.
I've put the full code for the module below. I had to shuffle a couple of other things around within this class for it to work, but it's essentially the same.
public class PerWebRequestLifestyleModule : IHttpModule
{
private const string key = "castle.per-web-request-lifestyle-cache";
private static bool allowDefaultScopeOutOfHttpContext = true;
private static bool initialized;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
initialized = true;
context.EndRequest += Application_EndRequest;
}
protected void Application_EndRequest(Object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var scope = GetScope(application.Context, createIfNotPresent: false);
if (scope != null)
{
scope.Dispose();
}
}
private static bool IsRequestAvailable()
{
if (HttpContext.Current == null)
{
return false;
}
try
{
if (HttpContext.Current.Request == null)
{
return false;
}
return true;
}
catch (HttpException)
{
return false;
}
}
internal static ILifetimeScope GetScope()
{
var context = HttpContext.Current;
if (initialized)
{
return GetScope(context, createIfNotPresent: true);
}
else if (allowDefaultScopeOutOfHttpContext && !IsRequestAvailable())
{
// We're not running within a Http Request. If the option has been set to allow a normal scope to
// be used in this situation, we'll use that instead
ILifetimeScope scope = CallContextLifetimeScope.ObtainCurrentScope();
if (scope == null)
{
throw new InvalidOperationException("Not running within a Http Request, and no Scope was manually created. Either run from within a request, or call container.BeginScope()");
}
return scope;
}
else if (context == null)
{
throw new InvalidOperationException(
"HttpContext.Current is null. PerWebRequestLifestyle can only be used in ASP.Net");
}
else
{
EnsureInitialized();
return GetScope(context, createIfNotPresent: true);
}
}
/// <summary>
/// Returns current request's scope and detaches it from the request context.
/// Does not throw if scope or context not present. To be used for disposing of the context.
/// </summary>
/// <returns></returns>
internal static ILifetimeScope YieldScope()
{
var context = HttpContext.Current;
if (context == null)
{
return null;
}
var scope = GetScope(context, createIfNotPresent: true);
if (scope != null)
{
context.Items.Remove(key);
}
return scope;
}
private static void EnsureInitialized()
{
if (initialized)
{
return;
}
var message = new StringBuilder();
message.AppendLine("Looks like you forgot to register the http module " + typeof(PerWebRequestLifestyleModule).FullName);
message.AppendLine("To fix this add");
message.AppendLine("<add name=\"PerRequestLifestyle\" type=\"Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor\" />");
message.AppendLine("to the <httpModules> section on your web.config.");
if (HttpRuntime.UsingIntegratedPipeline)
{
message.AppendLine(
"Windsor also detected you're running IIS in Integrated Pipeline mode. This means that you also need to add the module to the <modules> section under <system.webServer>.");
}
else
{
message.AppendLine(
"If you plan running on IIS in Integrated Pipeline mode, you also need to add the module to the <modules> section under <system.webServer>.");
}
#if !DOTNET35
message.AppendLine("Alternatively make sure you have " + PerWebRequestLifestyleModuleRegistration.MicrosoftWebInfrastructureDll +
" assembly in your GAC (it is installed by ASP.NET MVC3 or WebMatrix) and Windsor will be able to register the module automatically without having to add anything to the config file.");
#endif
throw new ComponentResolutionException(message.ToString());
}
private static ILifetimeScope GetScope(HttpContext context, bool createIfNotPresent)
{
var candidates = (ILifetimeScope)context.Items[key];
if (candidates == null && createIfNotPresent)
{
candidates = new DefaultLifetimeScope(new ScopeCache());
context.Items[key] = candidates;
}
return candidates;
}
}
Ok, I figured out a very clean way to do this!
First of all we'll need an implementation of IHandlerSelector, this can select a handler based on our opinion on the matter, or remain neutral (by returning null, which means "no opinion").
/// <summary>
/// Emits an opinion about a component's lifestyle only if there are exactly two available handlers and one of them has a PerWebRequest lifestyle.
/// </summary>
public class LifestyleSelector : IHandlerSelector
{
public bool HasOpinionAbout(string key, Type service)
{
return service != typeof(object); // for some reason, Castle passes typeof(object) if the service type is null.
}
public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
{
if (handlers.Length == 2 && handlers.Any(x => x.ComponentModel.LifestyleType == LifestyleType.PerWebRequest))
{
if (HttpContext.Current == null)
{
return handlers.Single(x => x.ComponentModel.LifestyleType != LifestyleType.PerWebRequest);
}
else
{
return handlers.Single(x => x.ComponentModel.LifestyleType == LifestyleType.PerWebRequest);
}
}
return null; // we don't have an opinion in this case.
}
}
I made it so the opinion is very limited on purpose. I'll be having an opinion only if there are exactly two handlers and one of them has PerWebRequest lifestyle; meaning the other one is probably the non-HttpContext alternative.
We need to register this selector with Castle. I do so before I start registering any other components:
container.Kernel.AddHandlerSelector(new LifestyleSelector());
Lastly I wish I had any clue as to how I could copy my registration to avoid this:
container.Register(
AllTypes
.FromAssemblyContaining<EmailService>()
.Where(t => t.Name.EndsWith("Service"))
.WithService.Select(IoC.SelectByInterfaceConvention)
.LifestylePerWebRequest()
);
container.Register(
AllTypes
.FromAssemblyContaining<EmailService>()
.Where(t => t.Name.EndsWith("Service"))
.WithService.Select(IoC.SelectByInterfaceConvention)
.LifestylePerThread()
);
If you can figure out a way to clone a registration, change the lifestyle and register both of them (using either container.Register or IRegistration.Register), please post it as an answer here! :)
Update: In testing, I need to uniquely name the identical registrations, I did so like this:
.NamedRandomly()
public static ComponentRegistration<T> NamedRandomly<T>(this ComponentRegistration<T> registration) where T : class
{
string name = registration.Implementation.FullName;
string random = "{0}{{{1}}}".FormatWith(name, Guid.NewGuid());
return registration.Named(random);
}
public static BasedOnDescriptor NamedRandomly(this BasedOnDescriptor registration)
{
return registration.Configure(x => x.NamedRandomly());
}
I don't know whats happening behind the scenes in .LifestylePerWebRequest(); but this is what I do for "Context per request" scenarios:
Check HttpContext for session and if exists pull the context from .Items.
If it doesn't exist pull your context from System.Threading.Thread.CurrentContext.
Hope this helps.
I use the following, static class to access the data context in my application
public static class DataContext
{
internal const string _contextDataKey = "dataContext";
/// <summary>
/// Returns a unique data context that lives for the duration of the request, which can be from ASP.NET or a WCF service
/// </summary>
/// <returns>The entity data model context for the current request</returns>
public static EntityDataModelContext GetDataContext()
{
IPersistanceContainer state;
if (HttpContext.Current != null)
{
state = new AspNetPersistanceContainer();
}
else if (OperationContext.Current != null)
{
state = new WcfPersistanceContainer();
}
else
{
state = new StaticPersistanceContainer(); // this container is thread-unsafe.
}
EntityDataModelContext edm = state.Get<EntityDataModelContext>(_contextDataKey);
if (edm == null)
{
edm = new EntityDataModelContext();
state.Store(_contextDataKey, edm);
}
return edm;
}
}
Forget about the other containers, which are for WCF and Console application simple-tests respectively, here's the ASP.NET container:
internal class AspNetPersistanceContainer : IPersistanceContainer
{
public T Get<T>(string key) where T : class
{
if (HttpContext.Current.Items.Contains(key))
return (T)HttpContext.Current.Items[key];
return null;
}
public void Store(string key, object value)
{
HttpContext.Current.Items[key] = value;
}
}
When I need to access the context I just invoke DataContext.GetDataContext() and do my DB-accessing, I never add any using statements.
If I add a using statement, the context is good for one use, and the next time I try to use it, it's disposed of. Raising an exception.
If I don't, like right now, it makes me kind of unhappy, I feel like it's not the right thing to do either, not disposing of it.
So I was wondering what would be the correct thing to do here.
Is this design flawed, and should I abandon it altogether?
Should I just figure out a way to re-create the context whenever it's disposed of?
Should I just leave the design as is, and that's fine?
Maybe the design is "fine enough", are there any books that you'd recommend I read on the subject? I feel like my skills on back-end architecture are rather on the lacking side.
In an asp.net application one solution can be like this :
Create your context when a request begins
Dispose it when the request ends
Here's an article that discuss this approach (for NHibernate session management but it's almost the same for EF )
So here is the problem. I have a common class library that holds all the repositories, domain and mapping files so the library can be reused within other web beased applications. Now within this class library there is a peiece of code that allows itself to create a session factory to be used in its repositories. Code looks something like this.
private static ISessionFactory sessionFactory;
private static Configuration configuration;
public static Configuration Configuration()
{
if (configuration == null)
{
configuration = new Configuration().Configure();
}
return configuration;
}
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
sessionFactory = Configuration().BuildSessionFactory();
}
return sessionFactory;
}
}
public static ISession GetCurrentSession()
{
if (!CurrentSessionContext.HasBind(SessionFactory))
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
}
return SessionFactory.GetCurrentSession();
}
So the repository calls the GetCurrentSession() method to get a ISession. Now this works fine but I am worried that it might not be thread safe. Can any one help me with an approach that will help me make it thread safe.
Few things to Note:
I have thought about configuring and building the SessionFactory in global.asax of the web applications on start event but the problem with this is that the common class library in question is used within 20 different applications so this will mean going to all the applications and updating the global.asax file now before I do this I wanted to put the question out there to see if there any other ways I can go about this. So that the common class library can configure its SessionFactory itself and yet be thread safe.
Thanks for reading this huge question. Will appericate any help.
The session factory is threadsafe, the session is not. Building the session factory needs to be protected:
private static object lockObject = new object();
private static ISessionFactory SessionFactory
{
get
{
lock (lockObject)
{
if (sessionFactory == null)
{
sessionFactory = Configuration().BuildSessionFactory();
}
return sessionFactory;
}
}
}
The session factory is created the first time a thread is requesting a session. This needs to be thread safe to avoid creating the session factory multiple times.
Creating the session by the session factory is thread safe, so you don't need to worry about that.
Sessions are not thread safe in NHibernate by design. So it should be ok as long as you have a session used by only one thread.
You can have one NHibernate SessionFactory for multiple threads as long as you have a separate NHibernate session for each thread
for more info have a look at the below link:
https://forum.hibernate.org/viewtopic.php?p=2373236&sid=db537baa5a57e3968abdda5cceec2a24
I suggest use one session for each Request like this:
public ISession GetCurrentSession()
{
HttpContext context = HttpContext.Current;
var currentSession = context.Items["session"] as ISession;
if( currentSession is null )
{
currentSession = SessionFactory.GetCurrentSession()
context.Items["session"] = currentSession;
}
return currentSession;
}
Following on from the comment by Stefan Steinegger, I think it would be more efficient to add a null check immediately before the lock, that way you don't need to lock every time if the sessionFactory has already been initialized.
private static object lockObject = new object();
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory != null)
{
return sessionFactory;
}
lock (lockObject)
{
if (sessionFactory == null)
{
sessionFactory = Configuration().BuildSessionFactory();
}
return sessionFactory;
}
}
}
I'm working with single-tier, single-user applications, with FluentNHibernate. With multiple threads, triggered by time triggers and incoming socket message triggers.
What requirements will determine if I can create/dispose the ISession inside each method of the repositories, or if I need to maintain the ISession lifecycle over multiple calls, maybe from program start to end?
For example, does lazy-load require session to be maintained? And if I don't use lazyload, for what other reason should I maintain the ISession?
Currently my repository methods look like below, but I wonder if I'm doing it wrong..
public class ProductRepository
{
public void Delete(Product product)
{
using (ISession session = FNH_Manager.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Delete(product);
transaction.Commit();
}
}
}
class FNH_Manager
{
private static Configuration cfg;
private static ISessionFactory sessionFactory;
public static void ConfigureSessionFactory()
{
sessionFactory = CreateSessionFactory();
}
public static ISession OpenSession()
{
return sessionFactory.OpenSession();
}
EDIT1:
Attempt to handle "session per call":
public class EmployeeRepository
{
public static void Delete(Employee employee)
{
using (ISession session = FNH_Manager.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
if (Employee.Id != 0)
{
var emp = session.Get(typeof(Employee), employee.Id);
if (emp != null)
{
session.Delete(emp);
transaction.Commit();
}
}
}
}
}
The session must be open when you reference a lazy-loaded field, so if you're relying on lazy-loading outside of your repository you'll need to manage the session lifespan somewhere higher up.
If you don't use lazy-loading, there's also the matter of whether you need to support multiple actions in one transaction. For example, if you delete a product AND some other data in one go, you'd want that to happen in one transaction in the same session (otherwise you might delete the product, have some code throw some exception, and never delete the other data, which may end up with orphan records or a corrupt state in your database).
I think you should use UnitOfWork pattern per thread.
On thread start create ISession and initialize UnitOfWork with it. Repositories use UnitOfWork with that signle ISession. At the end of thread execution commit the changes or rollback if there was conflict with other threads.
The Product is not associated with any session when beeing deleted. It is a so called detached object. To use it within the session for example deleting it you need to first associate it with the currently opened session. There are several ways to achive this:
Keep the session open. If the same session is opened when the Product is loaded as when it is deleted, it will work fine.
Reload the object, but using ISession.Get() or ISession.Load().
Re-attach the object to the newly opened session session with ISession.Lock()
Otherwise you'll probably get StaleStateExceptions and the like.
Remeber to read up on the NHibernate documentation
In this question I asked about NHibernate session lifetime. I'm using a desktop application, but with client/server separation, so the conclusion is that I will use one session per server request, as the server side is where all the NHibernate magic happens.
My problem now is how to handle it. I've had problems before with loading of referenced data when the session is prematurely closed. The problem is that I see the following on my referenced classes when debugging - hence the referenced data isn't loaded yet:
base {NHibernate.HibernateException} = {"Initializing[MyNamespace.Foo#14]-failed to lazily initialize a collection of role: MyNamespace.Foo.Bars, no session or session was closed"}
From what I understand it doesn't load all even though I commit the transaction. So I've learned that I need to keep my session open for a while, but how long?
My question is basically if I'm handling the lifetime properly, or what I should change to be on the right track. Honestly I can't see how this can be wrong, so what I'd really like is a function call to ensure that the referenced data is fetched. I'm not using lazy loading, so I thought they would be loaded immediately..?
Current architecture: Using a "service behavior" class that does the transaction. This is IDisposable, so the service itself it using a using-clause around it. The NHibernateSessionFactory provides a static factory which hence will be resused.
// This is the service - the function called "directly" through my WCF service.
public IList<Foo> SearchForFoo(string searchString)
{
using (var serviceBehavior = new FooServiceBehavior(new NhibernateSessionFactory()))
{
return serviceBehavior.SearchForFoo(searchString);
}
}
public class FooServiceBehavior : IDisposable
{
private readonly ISession _session;
public FooServiceBehavior(INhibernateSessionFactory sessionFactory)
{
_session = sessionFactory.OpenSession();
}
public void Dispose()
{
_session.Dispose();
}
public IList<Foo> SearchForFoo(string searchString)
{
using (var tx = _session.BeginTransaction())
{
var result = _session.CreateQuery("from Foo where Name=:name").SetString("name", searchString).List<Name>();
tx.Commit();
return result;
}
}
It turns out I'm doing lazy loading after all. I had the following mapping:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Not.LazyLoad();
Id(c => c.Id).GeneratedBy.HiLo("1");
Map(c => c.Name).Not.Nullable().Length(100);
HasMany(x => x.Bars).Cascade.All();
}
}
I assumed the Not.LazyLoad() disabled lazy loading, but apparently not for referenced objects. I added lazy loading on the reference, and this seems to have fixed the issue.
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Not.LazyLoad();
Id(c => c.Id).GeneratedBy.HiLo("1");
Map(c => c.Name).Not.Nullable().Length(100);
HasMany(x => x.Bars).Not.LazyLoad(); // <----------
}
}
Thanks for your time, and I'll still be glad to see your opinions to whether my given structure is reasonable.
If xml files are used for mapping purpose, we can set lazy=false for the bag.