i got problem with load and evict in session of hibernate here are the codes.
public virtual void ClearData(T obj)
{
using (ISession ses = SessionManager.OpenSession())
{
ses.Evict(obj);
}
}
public virtual T Load<T>(object id)
{
using (ISession ses = SessionManager.OpenSession())
{
return (T)ses.Load(typeof(T), id);
}
}
calling it with
Firmy fir = new Firmy();
fir.ClearData(fir);
var yol = fir.Load<Firmy>(6);
Response.Write("<br/><br/><br/> TEST get");
Response.Write(yol.NazwaFirmy);
Response.Write("<br/><br/><br/> TEST EVI");
fir.ClearData(yol);
Response.Write(yol.NazwaFirmy);
and here is session menager
public class SessionManager
{
#region Class Member Declarations
private static readonly ISessionFactory _sessionFactory;
private static readonly Configuration _configuration;
#endregion
static SessionManager()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof(SessionManager).Assembly);
_sessionFactory = _configuration.BuildSessionFactory();
}
public static ISession OpenSession()
{
return _sessionFactory.OpenSession();
}
#region Class Property Declarations
public static ISessionFactory SessionFactory
{
get { return _sessionFactory; }
}
#endregion
}
I wanna to load some data of fir with load function and then clear data with cleardata but idk how to do this was based on some tutroial.
Where do you exaclty get the error?
A couple of things that are not ok:
You're calling evict (first time) for an object that is not related to any nhibernate session (you just created it). Evict is for detaching an object from a session, but only make sense if you loaded the object with that session.
You create a session on every opearation and that's not the recommended way to go. You load an object with one session and then you try to evict it on a different session and that's not possible.
Related
I have an ASP.Net Web API project. I am using NHibernate in this project; Fluent NHibernate to be specific. I am handling NHib session management using a custom ActionFilterAttribute. It looks like this:
public class SessionManagement : ActionFilterAttribute
{
public SessionManagement()
{
SessionFactory = WebApiApplication.SessionFactory;
}
private ISessionFactory SessionFactory { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var transaction = session.Transaction;
if (transaction != null && transaction.IsActive)
{
transaction.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
This was working well for my needs. However, I have recently added a custom JSON.NET MediaTypeFormatter to format the my action's resulting JSON. The problem I am having is that my ActionFilter OnActionExecuted() method is called before the MediaTypeFormatter's WriteToStreamAsync can do it's job. The result is that lazily loaded (the problem) collections are now not available because the session is closed. What is the best way to handle this? Should I remove the ActionFilter's OnActionExecuted method and just close my session in the MediaTypeFormatter?
Thanks!!
The MediaTypeFormatter is the wrong layer to close the session at because this behavior really has nothing to do with the particular formatter you're using. Here's what I recommend doing:
Derive from HttpContent and create a class that derives from ObjectContent. Override the SerializeToStreamAsync implementation to await the base implementation's SerializeToStreamAsync then close the session:
public class SessionClosingObjectContent : ObjectContent
{
private Session _session;
public SessionClosingObjectContent(Type type, object value, MediaTypeFormatter formatter, Session session)
: base(type, value, formatter)
{
_session = session;
}
protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
await base.SerializeToStreamAsync(stream, context);
// Close the session and anything else you need to do
_session.Close();
}
}
Now in your action filter, instead of closing the session, you want to replace the response Content with your new class that closes the session:
public class SessionManagement : ActionFilterAttribute
{
public SessionManagement()
{
SessionFactory = WebApiApplication.SessionFactory;
}
private ISessionFactory SessionFactory { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var response = actionExecutedContext.Response;
if (response.Content != null)
{
ObjectContent objectContent = response.Content as ObjectContent;
if (objectContent != null)
{
response.Content = new SessionClosingObjectContent(objectContent.ObjectType, objectContent.Value, objectContent.Formatter, session);
foreach (KeyValuePair<string, IEnumerable<string>> header in objectContent.Headers)
{
response.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
}
}
}
You could also choose instead to return an HttpResponseMessage with your new Content directly from your controller code wherever you need it.
I'm creating an Asp.Net MVC 3 application with NHibernate as my ORM. In my my edit action method I call the Save method in my DatabaseAccessObject class, but instead of updating the object it creates a new one. I can't figure out why.
Here's the code for the method that returns my configured SessionFactory, and my global.asax.cs file where I'm storing the SessionFactory:
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString("Server=(local);Database=WebApplicationPbiBoard;Trusted_Connection=True;"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<WebApplicationPbiBoard.Models.ScrumModels_Mappings.PbiMap>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
.CurrentSessionContext("web")
.BuildSessionFactory();
}
public class MvcApplication : System.Web.HttpApplication
{
public static ISessionFactory SessionFactory { get; private set; }
protected void Application_Start()
{
//my additions
SessionFactory = NHibernateConfigurator.CreateSessionFactory();
}
protected void Application_OnEnd()
{
SessionFactory.Dispose();
}
protected void Application_BeginRequest()
{
ISession session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
}
protected void Application_EndRequest()
{
CurrentSessionContext.Unbind(SessionFactory);
}
}
Here's the relevant snippet from my DataAccessObject, which simply wraps NHibernate CRUD operations:
public class DatabaseAccessObject<T> where T : class
{
private readonly ISession session = MvcApplication.SessionFactory.GetCurrentSession();
private ISession Session { get { return session; } }
public T Save(T obj)
{
ITransaction transaction = null;
try
{
transaction = Session.BeginTransaction();
Session.SaveOrUpdate(obj);
transaction.Commit();
return obj;
}
catch (Exception ex)
{
if (transaction != null && transaction.IsActive)
transaction.Rollback();
throw;
}
}
And finally, here's the code for my Http-Post edit method:
private readonly DatabaseAccessObject<Sprint> db = new DatabaseAccessObject<Sprint>();
private DatabaseAccessObject<Sprint> Db { get { return db; } }
[HttpPost]
public ActionResult Edit(Sprint editedSprint)
{
if (ModelState.IsValid)
{
Db.Save(editedSprint);
return RedirectToAction("Index");
}
else
return View(editedSprint);
}
Any help would be appreciated.
The object you're saving probably hasn't got the Id set on it.
SaveOrUpdate does one of two things:
- Update() if the Id is set.
- Save() if the Id is not set.
refer to the docs:
http://www.nhforge.org/doc/nh/en/index.html
I'm getting a sporadic error that is difficult to reproduce. My first guess is that somehow I have a leaking nhibernate session, however when I ran the nhibernate profiler, I didn't see much out of the ordinary.
MVC 2.0
Fluent version 1.1.0.685
NHibernate version 2.1.2.4000
Exception: System.ArgumentException :
An item with the same key has already
been added.
Stack Trace: at
System.Collections.Generic.Dictionary2.Insert(TKey
key, TValue value, Boolean add) at
NHibernate.Util.ThreadSafeDictionary2.Add(TKey
key, TValue value) at
NHibernate.SqlTypes.SqlTypeFactory.GetTypeWithLen[T](Int32
length, TypeWithLenCreateDelegate
createDelegate) at
NHibernate.Type.EnumStringType..ctor(Type
enumClass, Int32 length)
I am using a repository model. Here's my repository class.
public sealed class Repository<T> : IRepository<T> where T : CoreObjectBase
{
#region IRepository<T> Members
private ISession Session
{
get
{
return new SessionHelper().GetSession();
}
}
public IQueryable<T> GetAll()
{
return (from entity in Session.Linq<T>() select entity);
}
public T GetById(int id)
{
return Session.Get<T>(id);
}
public void Save(params T[] entities)
{
using (ITransaction tx = Session.BeginTransaction())
{
for (int x = 0; x < entities.Count(); x++)
{
var entity = entities[x];
entity.Validate();
Session.SaveOrUpdate(entities[x]);
if (x == entities.Count() - 1 || (x != 0 && x % 20 == 0)) //20 is the batch size
{
Session.Flush();
Session.Clear();
}
}
tx.Commit();
}
}
public void SaveWithDependence<K>(T entity, K dependant) where K : CoreObjectBase
{
entity.Validate();
dependant.Validate();
using (ITransaction tx = Session.BeginTransaction())
{
Session.SaveOrUpdate(entity);
Session.SaveOrUpdate(dependant);
tx.Commit();
}
}
public void Save(T entity)
{
entity.Validate();
using (ITransaction tx = Session.BeginTransaction())
{
Session.SaveOrUpdate(entity);
tx.Commit();
}
}
public void Delete(T entity)
{
using (ITransaction tx = Session.BeginTransaction())
{
Session.Delete(entity);
tx.Commit();
}
}
public T GetOne(QueryBase<T> query)
{
var result = query.SatisfyingElementFrom(Session.Linq<T>());
return result;
//return query.SatisfyingElementFrom(Session.Linq<T>());
}
public IQueryable<T> GetList(QueryBase<T> query)
{
return query.SatisfyingElementsFrom(Session.Linq<T>());
}
/// <summary>
/// remove the sepcific object from level 1 cache so it can be refreshed from the database
/// </summary>
/// <param name="entity"></param>
public void Evict(T entity)
{
Session.Evict(entity);
}
#endregion
}
And here is my session helper, adapted from this.
public sealed class SessionHelper
{
private static ISessionFactory _sessionFactory;
private static ISession _currentSession;
public ISession GetSession()
{
ISessionFactory factory = getSessionFactory();
ISession session = getExistingOrNewSession(factory);
return session;
}
private ISessionFactory getSessionFactory()
{
if (_sessionFactory == null)
{
_sessionFactory = BuildSessionFactory();
}
return _sessionFactory;
}
private ISessionFactory BuildSessionFactory()
{
return Fluently.Configure().Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005
.ConnectionString(c => c
.FromConnectionStringWithKey("MyDatabase"))
.AdoNetBatchSize(20))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionHelper>())
.BuildSessionFactory();
}
private ISession getExistingOrNewSession(ISessionFactory factory)
{
if (HttpContext.Current != null)
{
ISession session = GetExistingWebSession();
if (session == null)
{
session = openSessionAndAddToContext(factory);
}
else if (!session.IsOpen)
{
session = openSessionAndAddToContext(factory);
}
return session;
}
if (_currentSession == null)
{
_currentSession = factory.OpenSession();
}
else if (!_currentSession.IsOpen)
{
_currentSession = factory.OpenSession();
}
return _currentSession;
}
public ISession GetExistingWebSession()
{
return HttpContext.Current.Items[GetType().FullName] as ISession;
}
private ISession openSessionAndAddToContext(ISessionFactory factory)
{
ISession session = factory.OpenSession();
HttpContext.Current.Items.Remove(GetType().FullName);
HttpContext.Current.Items.Add(GetType().FullName, session);
return session;
}
}
Any ideas or suggestions to avoid this issue?
Problem is, that SessionHelper isn't thread-safe. It will potentially build several session factories (it's a bad implementation of Singleton), which in turn probably causes the error you're seeing.
I recommend using SharpArchitecture as guidance instead.
I ran into the same issue, "An item with the same key has already been added" while constructing the nhibernate configuration.
What was happening for me was that two threads were programmatically constructing different configurations, intended to connect to different databases, at the same time.
I added a lock around the entire configuration-maker, and the problem went away.
So I guess the configuration object depends on some internal global state, i.e. assumes that the configuration itself is a singleton (as i guess it would be, if it were totally file-driven, as opposed to programmatically constructed).
I realize that this is an old question but I had a similar error just a few days ago, using NHibernate 3.0.
For readers that may stumble upon this issue: this is the result of a known thread-safety problem in older versions of NHibernate. This was fixed in version 3.2 but older versions will not have the fix and may produce this problem. This bug entry describes the issue: https://nhibernate.jira.com/browse/NH-3271
I went the route that #joel truher wrote about. But, I wanted to put the code here on how to do it.
public class NHibernateBootstrapper
{
private static readonly object _sessionFactoryLock = new object();
private static ISessionFactory _sessionFactory;
public static ISessionFactory CreateThreadStaticSessionFactory(string connectionString, bool exportSchema)
{
lock (_sessionFactoryLock)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)
.AdoNetBatchSize(16))
.CurrentSessionContext<ThreadStaticSessionContext>()
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<NHibernateBootstrapper>()
.Conventions.AddFromAssemblyOf<NHibernateBootstrapper>();
m.HbmMappings.AddFromAssemblyOf<NHibernateBootstrapper>();
})
.ExposeConfiguration(cfg => BuildSchema(cfg, exportSchema))
.BuildSessionFactory();
}
return _sessionFactory;
}
}
}
Obviously, you can configure your database however you'd like.
I have created an multi thread application on IIS (ASP.NET MVC), When the threading server started it creates 10 thread and it's execting workitems into the threads.
Usually my application working well, but some time i have got errors and i'm sure that problem is coming from fluent configuration. And I'm sure again i have made some mistake :)
Here is the my SessionFactory Class :
public class NHibernateHelper
{
private static ISessionFactory sessionFactory;
/// <summary>
/// SessionFactory is static because it is expensive to create and is therefore at application scope.
/// The property exists to provide 'instantiate on first use' behaviour.
/// </summary>
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
sessionFactory = CreateSessionFactory();
}
return sessionFactory;
}
}
/// <summary>
/// CreateSessionFactory
/// </summary>
/// <returns></returns>
private static ISessionFactory CreateSessionFactory()
{
IPersistenceConfigurer dbConfigurer = MsSqlConfiguration.MsSql2005
.ConnectionString("connection string ..")
.Cache(c => c
.UseQueryCache()
.ProviderClass<NoCacheProvider>()
).ShowSql()
.CurrentSessionContext<ThreadStaticSessionContext>();
return Fluently
.Configure()
.Database(dbConfigurer)
.Mappings(mc =>
{
mc.FluentMappings.Add(typeof(UserMap));
mc.FluentMappings.Add(typeof(ApplicationMap));
mc.FluentMappings.Add(typeof(SubscriptionsMap));
})
.BuildSessionFactory();
}
public static ISession GetCurrentSession()
{
if (!CurrentSessionContext.HasBind(SessionFactory))
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
}
return SessionFactory.GetCurrentSession();
}
public static void DisposeSession()
{
var session = GetCurrentSession();
session.Close();
session.Dispose();
}
public static void BeginTransaction()
{
GetCurrentSession().BeginTransaction();
}
public static void CommitTransaction()
{
var session = GetCurrentSession();
if (session.Transaction.IsActive)
session.Transaction.Commit();
}
public static void RollbackTransaction()
{
var session = GetCurrentSession();
if (session.Transaction.IsActive)
session.Transaction.Rollback();
}
}
Every thread is calling NHibernateHelper class with this line inside of itself ;
var myobjectinstance = new ObjectInstance();
NHibernateHelper.GetCurrentSession().Save( myobjectinstance );
I saw that, when i started server some time it has invoking 300.000 work item for test purpose successfully. But sometime it's giving errors about 2-3 workitem.
The exception is :
[0] = {"An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.\r\n\r\n"}
Inner excetion is :
Object reference not set to an instance of an object.
Inner exception stack trace is :
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.set_Item(TKey key, TValue value)
at NHibernate.Impl.SessionFactoryObjectFactory.AddInstance(String uid, String name, ISessionFactory instance, IDictionary`2 properties)
at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
at NHibernate.Cfg.Configuration.BuildSessionFactory()
at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory()
in d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentConfiguration.cs:line 93
Any suggestion or help are welcome
It looks like the CreateSessionFactory method is called multiple times. The sessionFactory static field access is not synchronized in this method which makes it not thread safe:
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
sessionFactory = CreateSessionFactory();
}
return sessionFactory;
}
}
Make sure to always synchronize access to shared resources in multithreaded applications. There's a common used pattern in this situation called singleton.
For some reason the Nhibernate is chocking out when I try to access the session. It is throwing the following exception:
No CurrentSessionContext configured (set the property current_session_context_class)!
Please note I am not using XML to setup the configuration!
I am opening the session in my test:
[SetUp]
public void Initialize()
{
_session = GetSessionFactory().OpenSession();
_transaction = _session.BeginTransaction();
SetupContext();
When();
}
and then I am using Repository to access the current session. The repository is in different dll.
public void Save(Category newCategory)
{
var session = SessionFactory.GetSession();
session.SaveOrUpdate(newCategory);
}
public static ISession GetSession()
{
var session = _sessionFactory.GetCurrentSession();
if (session == null)
return _sessionFactory.OpenSession();
return session;
}
UPDATE:
In my BaseTest.cs class I also have a teardown:
[TearDown]
public void CleanUp()
{
_session.Dispose();
_transaction.Dispose();
}
During debugging it seems like the CleanUp is being fired and killing the _session object!
Another update: I have added the following code when building the configuration:
public static ISessionFactory CreateSessionFactory()
{
_sessionFactory =
Fluently.Configure().Database(
MsSqlConfiguration.MsSql2000.ConnectionString(
c => c.FromConnectionStringWithKey("ConnectionString")))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<Category>())
**.ExposeConfiguration(x =>
{
x.SetProperty("current_session_context_class",
"thread_static");
})**
.BuildSessionFactory();
return _sessionFactory;
}
Now I get the following error:
No session bound to the current context
You need to bind the session to the current context.
In the setup method:
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
And in the teardown method:
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();