I am trying to setup a Session factory with Nhibernate code by mapping, but I have issues configuring it, and its pretty hard to find guides to Code by mapping with the session factory.
Atm. I have this SessionManager, but I am uncertain where to specify its a MySQL database, proberly miss more.
public class SessionManager
{
private const string ConnString = "Server=localhost; Port=3306; Database=test; Uid=root; Pwd=123456;";
public static SessionManager CurrentInstance
{
get
{
if (_currentInstance == null)
{
object sync = new object();
lock (sync)
_currentInstance = new SessionManager();
}
return _currentInstance;
}
}
public static ISession Session
{
get
{
if (_sessionFactory == null)
{
object sync = new object();
lock (sync)
_sessionFactory = new Configuration()
.DataBaseIntegration(x => x.ConnectionString = ConnString)
.Configure()
.AddAssembly(typeof(EmployeeMap).Assembly)
.BuildSessionFactory();
}
return _sessionFactory.OpenSession();
}
}
private SessionManager() { }
static SessionManager _currentInstance;
static ISessionFactory _sessionFactory;
}
I think what you are trying to do is specify that you are using mysql. When I have done this I have used an NHibernate confiuration file with a line stating the driver_class:
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
Try this tutorial
http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx
it describes the process of setting up your nhibernate session and shows a sample hibernate.cfg.xml file. In this set up you need to specify the MySqlDataDriver instead of the SQLServerCeDriver shown.
I don't know if you can do this without having to use a hibernate.cfg.xml
Related
I want to cache the connection string and used cached object throughout my project. I tried like below
public static void Demo()
{
Hashtable Hashtable = new Hashtable()
Hashtable.Add("WEBConnectionString", ConfigurationManager.ConnectionStrings["WEBConnectionString"].ConnectionString);
HttpContext.Current.Application["CachedValue"] = Hashtable;}
public static string Method(string key)
{
string result = string.Empty;
Hashtable CachedObject = (Hashtable)HttpContext.Current.Application["CachedValue"];
if (CachedObject != null && CachedObject.ContainsKey(key))
{
result = CachedObject[key].ToString();
}
return result;
}
and accessing like this
string conString = Utility.Method("WEBConnectionString");
but CachedObject.ContainsKey(key) condition getting false. What am I doing wrong here? or is there any other method to cache the connection string.
This should work (in a somewhat generic way):
public class HttpContextCache
{
public void Remove(string key)
{
HttpContext.Current.Cache.Remove(key);
}
public void Store(string key, object data)
{
HttpContext.Current.Cache.Insert(key, data);
}
public T Retrieve<T>(string key)
{
T itemStored = (T)HttpContext.Current.Cache.Get(key);
if (itemStored == null)
{
itemStored = default(T);
}
return itemStored;
}
}
Anywhere you find appropriate in your code:
// cache the connection string
HttpContextCache cache = new HttpContextCache();
cache.Store("WEBConnectionString", ConfigurationManager.ConnectionStrings["WEBConnectionString"].ConnectionString);
// ...
// get connection string from the cache
HttpContextCache cache = new HttpContextCache();
string conString = cache.Retrieve<string>("WEBConnectionString");
My first thought is why would you cache it? It's configuration data and should be quick enough to fetch every time you need it.
If you really need caching, there are more modern alternatives to HttpContext.Current.Application.
You can use a IOC container as suggested in the comments and configure it as a singletion instance. Seems overkill to setup a IOC container for that purpose though. If you're having multiple servers and you want to make sure they have the same state consider using a distributed cache like Redis.
Other alternatives are storing the connection string in a static variable, use MemoryCache, HttpRuntime.Cache or HttpContext.Current.Cache.
Example using a lazy static variable:
private static Lazy<string> ConnectionString = new Lazy<string>(() => ConfigurationManager.ConnectionStrings["YourConnectionString"].ConnectionString);
// Access the connection string: var connectionString = ConnectionString.Value;
What is the custom wen the record inserting/updating is carried out?
I have this Log table in the MS SQL server database, and a C# class (example is simplified)
[Table(Name = "dbo.Sys_Log")]
public class Sys_Log
{
// Read-only, db-generated primary key ID
private int _logID;
[Column(IsPrimaryKey=true, Storage="_logID", IsDbGenerated=true)]
public int logID
{
get
{
return this._logID;
}
}
// Read-only db-generated datetime field
private System.DateTime _logTime;
[Column(Storage="_logTime", IsDbGenerated=true)]
public System.DateTime logTime
{
get
{
return this._logTime;
}
}
// Read-write string field
private string _logEvent;
[Column(Storage="_logEvent")]
public string logEvent
{
get
{
return this._logEvent;
}
set
{
this._logEvent = value;
}
}
public Sys_Log() {}
public Sys_Log(string logEvent)
{
this.logEvent = logEvent;
}
}
And this is how I add a log entry:
Table<Sys_Log> linqLog = db.GetTable<Sys_Log>();
Sys_Log l = new Sys_Log("event");
linqLog.InsertOnSubmit(l);
db.SubmitChanges();
I am not particularly happy about this code. I'd like something like this instead:
Sys_Log.Log("event");
I have idea how this can be achieved, but I'd like to know if I am following the LINQ philosophy. With this code added to the Sys_Log class
private static DataContext db;
public static void Connect(DataContext db)
{
Sys_Log.db = db;
}
public static void Log(string logEvent)
{
Table<Sys_Log> linqLog = db.GetTable<Sys_Log>();
Sys_Log l = new Sys_Log(logEvent);
linqLog.InsertOnSubmit(l);
db.SubmitChanges();
}
I can now do this:
Sys_Log.Connect(db); // Only once, at init
Sys_Log.Log("event1");
Sys_Log.Log("event2");
Are there any pitfalls, apart from the fact that the database is updated several times, that could be considered ineffective?
************** Update ******************
Following the advice of #usr not to reuse the DataContext object, I have made these changes to the Sys_Log class:
private static SqlConnection db;
public static void Connect(SqlConnection db)
{
Sys_Log.db = db;
}
public static void Log(string logEvent)
{
DataContext ctx = new DataContext(db);
ctx.CommandTimeout = 240;
Table<Sys_Log> linqLog = ctx.GetTable<Sys_Log>();
Sys_Log l = new Sys_Log(logEvent);
linqLog.InsertOnSubmit(l);
ctx.SubmitChanges();
}
Use a fresh data context each time. Reusing the same context has to catastrophic consequences:
No entity memory is ever released
When an invalid entity enters the context (due to a bug) it is stuck and will forever prevent SubmitChanges from succeeding. The application will never recover
Also note, that L2S is deprecated and EF has superseded it.
You can share a SqlConnection and use it long-term if you really want. That requires, through, that you deal with broken connections. Thanks to connection pooling there are little performance incentives to do this.
It usually is the easiest and most clear way to use throw-away connections. Inject a factory, for example:
Func<SqlConnection> myFactory = () => new SqlConnection(myConnStr);
That's all there is to it. Use it, as always, with using:
using(var conn = myFactory()) { ... }
I'm using Fluent nHibernate and Oracle database. I's working fine, but there is one problem, that i can't figure out.
I'm trying to create new session, after previos has been killed. But there is no new session in session list in pl/sql developer and query in second session throw exception like in first one. So my question is what i'm doing wrong or what i'm missing.
public static class FluentNHibernateHelper
{
private static ISessionFactory _sessionFactory;
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var dbConfig = OracleDataClientConfiguration.Oracle10
.ConnectionString(c => c.Is(RmsConnection.ConnectionString))
.ShowSql()
.FormatSql()
.Driver<OracleDataClientDriver>();
_sessionFactory = Fluently.Configure()
.Database(dbConfig)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<RmsDu.Data.Model.MessageHead>())
.BuildSessionFactory();
}
return _sessionFactory;
}
}
/// <summary>
/// Open new db session
/// </summary>
/// <returns></returns>
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
[Test]
public void SessionKillTest()
{
try
{
using (var session = FluentNHibernateHelper.OpenSession())
{
var q = session.Query<Data.Model.MessageType>();
//Here we kill first session
q.ToList();
}
}
catch (Exception ex) {}
using (var session = FluentNHibernateHelper.OpenSession())
{
var q = session .Query<Data.Model.MessageType>();
q.ToList();
}
}
The answer is that you have to dispose session.Connection manually before session dispose
I am very new (i.e. an hour or so) to NHibernate. I've followed a tutorial which gave me the following class:
public class ContactNHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof (CRMData.Objects.Contact).Assembly);
_sessionFactory = configuration.Configure().BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
In extending my application, I now have another class for a different object. I'm trying to rewrite the above class so that I can pass in the assembly type as a parameter and return the _sessionFactory. So, for example, I would have a variable passed in to the method called assembly. The code would then be:
public class GenericNHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory(System.Reflection.Assembly assembly)
{
get
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(assembly);
_sessionFactory = configuration.Configure().BuildSessionFactory();
}
return _sessionFactory;
}
}
}
This is giving the error 'Cannot resolve symbol 'get'' - presumably because I cannot pass any parameters in this way.
I am probably missing something very simple - any ideas?
You don't need to make any changes if your other class is in the same assembly as CRMData.Objects.Contact.
But if you want to pass in a parameter you need to convert the SessionFactory property to a method or create a constructor that accepts the parameter.
private static ISessionFactory SessionFactory(System.Reflection.Assembly assembly)
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(assembly);
_sessionFactory = configuration.Configure().BuildSessionFactory();
}
return _sessionFactory;
}
we dont have parameter properties in C# other than indexers.That is the reason you are getting the error.change your code to use a method.
But i still dont understand why you need to pass an assembly to the method.
For a web application, it seems like a good way to handle the session is to use the setting <property name="current_session_context_class">managed_web</property>, call CurrentSessionContext.Bind/Unbind on Begin/EndRequest. Then I can just use sessionFactory.GetCurrentSession() in the repository class.
This works fine for all page request. But I have background workers doing stuff and using the same repository classes to do stuff. These do not run within a web request, so that session handling won't work.
Any suggestions to how this can be solved?
I solved it by creating my own session context class:
public class HybridWebSessionContext : CurrentSessionContext
{
private const string _itemsKey = "HybridWebSessionContext";
[ThreadStatic] private static ISession _threadSession;
// This constructor should be kept, otherwise NHibernate will fail to create an instance of this class.
public HybridWebSessionContext(ISessionFactoryImplementor factory)
{
}
protected override ISession Session
{
get
{
var currentContext = ReflectiveHttpContext.HttpContextCurrentGetter();
if (currentContext != null)
{
var items = ReflectiveHttpContext.HttpContextItemsGetter(currentContext);
var session = items[_itemsKey] as ISession;
if (session != null)
{
return session;
}
}
return _threadSession;
}
set
{
var currentContext = ReflectiveHttpContext.HttpContextCurrentGetter();
if (currentContext != null)
{
var items = ReflectiveHttpContext.HttpContextItemsGetter(currentContext);
items[_itemsKey] = value;
return;
}
_threadSession = value;
}
}
}
I've found it simplest in this scenario to handle session creation myself using a DI library and 'hybrid' scope (in StructureMap, this is defined as InstanceScope.Hybrid). This will scope instances by HttpContext in an ASP.net app domain, and ThreadStatic in a normal app domain, allowing you to use the same approach in both.
I'm sure other DI libraries offer a similar feature.
On my project, I wrote a little wrapper class around the CurrentSessionContext.
Perhaps you can extend it to suit your needs.
I think you just need to tweak the implementation of BindSessionToRequest and GetCurrentSession:
public static class SessionManager
{
private static ISessionFactory _sessionFactory = null;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
//check whether we're in web context or win context, and create the session factory accordingly.
if (System.Web.HttpContext.Current != null)
{
if (_sessionFactory == null)
{
_sessionFactory = DAOBase.GetSessionFactory();
}
}
else
{
_sessionFactory = DAOBase.GetSessionFactoryForWin();
}
}
return _sessionFactory;
}
}
public static void BindSessionToRequest()
{
ISession session = SessionManager.SessionFactory.OpenSession();
NHibernate.Context.CurrentSessionContext.Bind(session);
}
public static bool CurrentSessionExists()
{
return NHibernate.Context.CurrentSessionContext.HasBind(SessionFactory);
}
public static void UnbindSession()
{
ISession session = NHibernate.Context.CurrentSessionContext.Unbind(SessionManager.SessionFactory);
if (session != null && session.IsOpen)
{
session.Close();
}
}
public static ISession GetCurrentSession()
{
return SessionFactory.GetCurrentSession();
}
}