I'm developing an ASP.NET (Razor v2) Web Site, and using the WebMatrix.Data library to connect to a remote DB. I have the Database wrapped in a singleton, because it seemed like a better idea than constantly opening and closing DB connections, implemented like so:
public class DB
{
private static DB sInstance = null;
private Database mDatabase = null;
public static DB Instance
{
get
{
if (sInstance == null)
{
sInstance = new DB();
}
return sInstance;
}
}
private DB()
{
mDatabase = Database.Open("<Connection String name from web.config>");
return;
}
<Query Functions Go Here>
}
("Database" here refers to the WebMatrix.Data.Database class)
The first time I load my page with the form on it and submit, a watch of mDatabase's Database.Connection property shows the following: (Sorry, not enough rep to post images yet.)
http://i.stack.imgur.com/jJ1RK.png
The form submits, the page reloads, the submitted data shows up, everything is a-ok. Then I enter new data and submit the form again, and here's the watch:
http://i.stack.imgur.com/Zorv0.png
The Connection has been closed and its Connection String blanked, despite not calling Database.Close() anywhere in my code. I have absolutely no idea what is causing this, has anyone seen it before?
I'm currently working around the problem by calling Database.Open() before and Database.Close() immediately after every query, which seems inefficient.
The Web Pages framework will ensure that connections opened via the Database helper class are closed and disposed when the current page has finished executing. This is by design. It is also why you rarely see connections explicitly closed in any Web Pages tutorial where the Database helper is used.
It is very rarely a good idea to have permanently opened connections in ASP.NET applications. It can cause memory leaks. When Close is called, the connection is not actually terminated by default. It is returned to a pool of connections that are kept alive by ADO.NET connection pooling. That way, the effort required to instantiate new connections is minimised but managed properly. So all you need to do is call Database.Open in each page. It's the recommended approach.
Related
I am working on ASP.Net MVC project. I am making my database transactions with;
using (ISession session = FluentNHibernateHelper.OpenSession())
{
var user = session.Query<User>()
.FirstOrDefault(x => x.UserEmail == email && x.UserPassword == password); }
Instead of using this type of code block which is like open-close connection everytime, I want to open connection at runtime and I want to use that session variable everywhere. Maybe some codes in Application_Start()in Global.asax.cs?
I am open to your valuable ideas. Thank you for your help!
It's poor practice to leave a connection open nor maintain the state in the ORM across multiple transactions as state issues can crop up rather quickly as you make multiple requests against the same object & connection.
However, if you must, you could inject it as a Singleton service which would live longer than a single request. This is problematic for scaling and not recommended.
services.AddSingleton<ISession>(provider =>
{
return FluentNHibernateHelper.OpenSession()
});
More information: What is the difference between services.AddTransient, service.AddScoped and service.AddSingleton methods in ASP.NET Core?
I am creating dynamic connection strings in my project. They're created on the fly with the information provided specifically for every user. When the application first fires off, if a database doesn't exist (first time user logs on), a new database is created without problems with this initializer:
public DataContext() : base()
{
// ProxyCreation and LazyLoading doesn't affect the situation so
// comments may be removed
//this.Configuration.LazyLoadingEnabled = false;
//this.Configuration.ProxyCreationEnabled = false;
string conStr = GetDb();
this.Database.Connection.ConnectionString = conStr;
}
The problem is, with this method, I have to restart the application pool on the server and the new user should be the first accessor to the application.
I need the same thing without a requirement of restarting the app. Is that possible?
(This is a SPA using AngularJS on MVC views and WebApi as data provider - May be relevant somehow, so thought I should mention)
I already tried this, but this creates an error for EF and the application doesn't start at all...
You could try a little bit different approach to connect directly (and create) the right database.
public class DataContext : DbContext
{
public DataContext(DbConnection connection) : base(connection, true) { }
}
Here you create the DbContext already with the right connection.
Take also care because you need to specify to migrations that the right connection should be used (not the Web.Config connection but the connection that raised the database creation).
See the second overload here https://msdn.microsoft.com/en-US/library/hh829099(v=vs.113).aspx#M:System.Data.Entity.MigrateDatabaseToLatestVersion.
I have a concurrent dictionary List on my controller that stores list of online users. For example when Client A and Client B connects there are 2 online clients present on the list, but when i disconnect B and then re- connect again it must still show 2 online clients but in my case, only Client B detected as online user(disconnected then reconnected). I am using IIS server 7.5.. Please help me with this, do i need to use a database rather than dictionary? I think it resets the dictionary to zero if one user disconnects and reconnects again.... :( Below is my hub class code
public class Chat : Hub
{
//add online client
private static ConcurrentDictionary<string, string> personLists
= new ConcurrentDictionary<string, string>();
public void Connect(string Username, int ID)
{
string id = Context.ConnectionId;
if (!personLists.ContainsKey(Username))
{
personLists.TryAdd(Username, id);
Clients.Caller.viewOnlinePersons(personLists.Where(p => p.Key != Username));
Clients.Others.enters(Username);
}
else
{
string notif = "user: "+Username+" is already used";
Clients.Caller.onUse(notif);
}
The concurrent dictionary should work just fine. It'd be good if you could post some code, but if you go this route with concurrent dictionary, make sure it's static (the Hub classes get created and destroyed per signal) and I think it'd be better suited placed on the hub itself and private (and of course static, again). You can also use Dependency Injection with SignalR which will be a lot cleaner.
You'll only need database as a backplane if you plan on running the application on multiple servers where of course a memory of a single server is not accessible by the other servers and a duplicate dictionary would be created for each server, so in that case you need to take the storage and move it up a bit in the architecture to be accessible by all the servers.
ASP.NET/MONO MVC2 application standard ASP.NET Web cache is used to speed up database access:
string GetName() {
// todo: dedect if data has changed and invalidate cache
var name = (string)HttpContext.Current.Cache["Name"];
if (name!=null)
return name;
name = db.Query("SELECT name from mydata");
HttpContext.Current.Cache.Insert("Name", name);
return name;
}
mydata can changed by other application.
In this case this method returns wrong data.
How to detect if data is changed and return fresh data from PostgreSql database in this case ?
It is OK to clear whole Web cache if mydata has changed.
The best way to do this is likely with LISTEN and NOTIFY.
Have your app maintain a background worker with a persistent connection to the DB. In that connection, issue a LISTEN name_changed, then wait for notifications. If npgsql supports it it might offer a callback; otherwise you'll have to poll.
Add a trigger to the name table that issues a NOTIFY name_changed.
When your background worker gets the notification it can flush the cache.
You can even use the NOTIFY payload to invalidate only changed entries selectively.
I've been using NHibernate for a while now and have found from time to time that if I try to request two pages simultaniously (or as close as I can) it will occasionally error. So I assumed that it was because my Session management was not thread safe.
I thought it was my class so I tried to use a different method from this blog post http://pwigle.wordpress.com/2008/11/21/nhibernate-session-handling-in-aspnet-the-easy-way/ however I still get the same issues. The actual error I am getting is:
Server Error in '/AvvioCMS' Application.
failed to lazily initialize a collection, no session or session was closed
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: NHibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
Either that or no datareader is open, but this is the main culprit.
I've placed my session management class below, can anyone spot why I may be having these issues?
public interface IUnitOfWorkDataStore
{
object this[string key] { get; set; }
}
public static Configuration Init(IUnitOfWorkDataStore storage, Assembly[] assemblies)
{
if (storage == null)
throw new Exception("storage mechanism was null but must be provided");
Configuration cfg = ConfigureNHibernate(string.Empty);
foreach (Assembly assembly in assemblies)
{
cfg.AddMappingsFromAssembly(assembly);
}
SessionFactory = cfg.BuildSessionFactory();
ContextDataStore = storage;
return cfg;
}
public static ISessionFactory SessionFactory { get; set; }
public static ISession StoredSession
{
get
{
return (ISession)ContextDataStore[NHibernateSession.CDS_NHibernateSession];
}
set
{
ContextDataStore[NHibernateSession.CDS_NHibernateSession] = value;
}
}
public const string CDS_NHibernateSession = "NHibernateSession";
public const string CDS_IDbConnection = "IDbConnection";
public static IUnitOfWorkDataStore ContextDataStore { get; set; }
private static object locker = new object();
public static ISession Current
{
get
{
ISession session = StoredSession;
if (session == null)
{
lock (locker)
{
if (DBConnection != null)
session = SessionFactory.OpenSession(DBConnection);
else
session = SessionFactory.OpenSession();
StoredSession = session;
}
}
return session;
}
set
{
StoredSession = value;
}
}
public static IDbConnection DBConnection
{
get
{
return (IDbConnection)ContextDataStore[NHibernateSession.CDS_IDbConnection];
}
set
{
ContextDataStore[NHibernateSession.CDS_IDbConnection] = value;
}
}
}
And the actual store I am using is this:
public class HttpContextDataStore : IUnitOfWorkDataStore
{
public object this[string key]
{
get { return HttpContext.Current.Items[key]; }
set { HttpContext.Current.Items[key] = value; }
}
}
I initialize the SessionFactory on Application_Start up with:
NHibernateSession.Init(new HttpContextDataStore(), new Assembly[] {
typeof(MappedClass).Assembly});
Update
Thanks for your advice. I have tried a few different things to try and simplify the code but I am still running into the same issues and I may have an idea why.
I create the session per request as and when it is needed but in my global.asax I am disposing of the session on Application_EndRequest. However I'm finding the Application_EndRequest is being fired more than once while I am in debug at the end of loading a page. I thought that the event is only suppose to fire once at the very end of the request but if it isn't and some other items are trying to use the Session (which is what the error is complaining about) for whatever weird reason that could be my problem and the Session is still thread safe it is just being disposed of to early.
Anyone got any ideas? I did a google and saw that the VS development server does cause issues like that but I am running it through IIS.
While I haven't seen your entire codebase or the the problem you're trying to solve, a rethinking of how you are using NHibernate might be in order. From the documentation:
You should observe the following
practices when creating NHibernate
Sessions:
Never create more than one concurrent
ISession or ITransaction instance per
database connection.
Be extremely careful when creating
more than one ISession per database
per transaction. The ISession itself
keeps track of updates made to loaded
objects, so a different ISession might
see stale data.
The ISession is not threadsafe! Never
access the same ISession in two
concurrent threads. An ISession is
usually only a single unit-of-work!
That last bit is the most relevant (and important in the case of a multithreaded environment) to what I'm saying. An ISession should be used once for a small atomic operation and then disposed. Also from the documentation:
An ISessionFactory is an
expensive-to-create, threadsafe object
intended to be shared by all
application threads. An ISession is an
inexpensive, non-threadsafe object
that should be used once, for a single
business process, and then discarded.
Combining those two ideas, instead of storing the ISession itself, store the session factory since that is the "big" object. You can then employ something like SessionManager.GetSession() as a wrapper to retrieve the factory from the session store and instantiate a session and use it for one operation.
The problem is also less obvious in the context of an ASP.NET application. You're statically scoping the ISession object which means it's shared across the AppDomain. If two different Page requests are created within that AppDomain's lifetime and are executed simultaneously, you now have two Pages (different threads) touching the same ISession which is not safe.
Basically, instead of trying to keep a session around for as long as possible, try to get rid of them as soon as possible and see if you have better results.
EDIT:
Ok, I can see where you're trying to go with this. It sounds like you're trying to implement the Open Session In View pattern, and there a couple different routes you can take on that:
If adding another framework is not an issue, look into something like Spring.NET. It's modular so you don't have to use the whole thing, you could just use the NHibernate helper module. It supports the open session in view pattern. Documentation here (heading 21.2.10. "Web Session Management").
If you'd rather roll your own, check out this codeproject posting by Bill McCafferty: "NHibernate Best Practices". Towards the end he describes implementing the pattern through a custom IHttpModule. I've also seen posts around the Internet for implementing the pattern without an IHttpModule, but that might be what you've been trying.
My usual pattern (and maybe you've already skipped ahead here) is use a framework first. It removes lots of headaches. If it's too slow or doesn't fit my needs then I try to tweak the configuration or customize it. Only after that do I try to roll my own, but YMMV. :)
I can't be certain (as I'm a Java Hibernate guy) in NHibernate but in hibernate Session objects are not thread safe by design. You should open and close a session and never allow it out of the scope of the current thread.
I'm sure that patterns such as 'Open session view' have been implemented in .Net somewhere.
The other interesting issue is when you put a hibernate entity in the session. The problem here is that the session that it is attached to will be closed (or should be) on the request finishing. You have to reattach the entity to the new (hibernate) session if you wish to navigate any non loaded associations. This in it's self causes a new issue if two requests try to do this at the same time as something will blow up if you try to attach an entity to two sessions.
Hope this helps.
Gareth
The problem ended up being that my library for inversion of control was not managing the objects being created in HTTP context correctly so I was getting references for objects that should of not been available to that context. This was using Ninject 1.0, once I updated to Ninject 2.0 (beta) the problem was resolved.