I'm learning how to use ASP.NET 5 (vNext). In an attempt to do this, I'm working on a basic application. In this application, I'm trying to connect to the database from a couple of POCOs (Customer, Order, etc.) using Dapper. If I understand things correctly, its expensive to create, connect to, and tear down connections to a database. If this is true, I'm trying to figure out the recommended way to share a connection across multiple objects.
Currently, I have the following:
public class Order
{
private IDbConnection _connection;
public void Save()
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
_connection.Execute("[INSERTION SQL]");
}
}
public List<Order> FindByCustomerEmailAddress(string emailAddress)
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
return _connection.Query<List<Order>>("SELECT o.* FROM Order o, Customer c WHERE o.CustomerId=c.CustomerId AND c.EmailAddress='" + emailAddress + "'" );
}
}
}
public class Customer
{
private IDbConnection _connection;
public void Save()
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
_connection.Execute("[INSERTION SQL]");
}
}
public Customer FindByEmailAddress(string emailAddress)
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
return _connection.Query<Customer>("SELECT * FROM Customer WHERE EmailAddress='" + emailAddress + "'" );
}
}
}
I thought about creating a Database class that looks like this:
public static class Database
{
private static IDbConnection Connection { get; set; }
public static IDbConnection GetConnection()
{
if (Connection == null)
{
Connection = new SqlConnection("[MyConnectionString]");
Connection.Open();
}
return Connection;
}
}
public class Order
{
public void Save()
{
var connection = Database.GetConnection();
connection.Execute("[INSERTION SQL]");
}
public List<Order> FindByCustomerEmailAddress(string emailAddress)
{
var connection = Database.GetConnection();
return connection.Query<List<Order>>("SELECT ...");
}
}
However, after thinking about this, I'm not sure if this a good strategy for managing a database connection. The use of static in this manner seems dangerous. Yet, it seems like someone has had to solve this issue. But, nothing I've seen is explained so I do not understand if it actually works. Can someone share with me what the recommended approach for managing database connections in an efficient manner is?
Thank you!
Opening and closing connection to a database server is indeed expensive. However, .NET implements connection pooling just for this reason, and it is on by default. You can modify the setting of how many connection it should keep open (I don't recall the default).
So, if your connection string is the same, .NET will reuse an open connection from the pool and use that. If different, it'll create a new one.
Your first code is correct in using "using" so when you're done, the dispose/close will give the connection back to the pool.
See more about this here; https://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx
Related
I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
I am a rookie with ASP.NET currently developing monitoring system for SQL Server, and still working on retrieving job's information so I can show them on the page.
I tried to use Mr. Alexey Zimarev's code this code
And I wrote it on my controller like this
public class JobsController : Controller
{
static readonly string SqlServer = #"DESKTOP-PQD9KKN";
private ApplicationDbContext _context;
public JobsController()
{
_context = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
}
[Route("Customers/MigrateJob")]
public ActionResult MigrateJob()
{
ServerConnection conn = new ServerConnection(SqlServer);
Server server = new Server(conn);
JobCollection jobs = server.JobServer.Jobs;
foreach(Job job in jobs)
{
var jobactivity = new JobActivity
{
Name = job.Name,
IsEnabled = job.IsEnabled
....so on
};
_context.JobActivities.Add(jobactivity);
}
return RedirectToAction("List", "Jobs");
}
public ActionResult List()
{
var jobactivities = _context.JobActivities.ToList();
return View(jobactivities);
}
}
My approach is to store the job's information from SQL Server Agent to my JobActivity table using MigrateJob(). The problem is the job's information hasn't stored in my table yet without any error messages.
My Tools:
VS 2017
SQL Server ver 13
Any help would be appreciated :)
My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.
I am new to Nhibernate and slowing working my way thru learning it. I tried to implement a session manager class to help me get the session for my db calls. Below is the code for it. Can someone please say if this is architecturally correct and foresee any issue of scalability or performance?
public static class StaticSessionManager
{
private static ISession _session;
public static ISession GetCurrentSession()
{
if (_session == null)
OpenSession();
return _session;
}
private static void OpenSession()
{
_session = (new Configuration()).Configure().BuildSessionFactory().OpenSession();
}
public static void CloseSession()
{
if (_session != null)
{
_session.Close();
_session = null;
}
}
}
and in my data provider class, I use the following code to get data.
public class GenericDataProvider<T>
{
NHibernate.ISession _session;
public GenericDataProvider()
{
this._session = StaticSessionManager.GetCurrentSession();
}
public T GetById(object id)
{
using (ITransaction tx = _session.BeginTransaction())
{
try
{
T obj = _session.Get<T>(id);
tx.Commit();
return obj;
}
catch (Exception ex)
{
tx.Rollback();
StaticSessionManager.CloseSession();
throw ex;
}
}
}
}
and then
public class UserDataProvider : GenericDataProvider<User>
{
public User GetUserById(Guid uid)
{
return GetById(uid)
}
}
Final usage in Page
UserDataProvider udp = new UserDataProvider();
User u = udp.GetUserById(xxxxxx-xxx-xxx);
Is this something that is correct? Will instantiating lot of data providers in a single page cause issues?
I am also facing an issue right now, where if I do a same read operation from multiple machines at the same time, Nhibernate throws random errors- which I think is due to transactions.
Please advice.
From what I can see you are building the session factory if you have a null session. You should only call BuildSessionFactory() once when the application starts.
Where you do this is up to you, some people build the SessionFactory inside Global.asax in the method application_start or in your case have a static property for sessionFactory instead of session in your StaticSessionManager class.
I suspect your errors are due to the fact that your session factory is being built multiple times!
Another point is that some people open a transaction _session.BeginTransaction() at the beginning of each request and either commit or rollback at the end of each request. This gives you a unit of work which means you can lose the
using (ITransaction tx = _session.BeginTransaction())
{
...
}
on every method. All of this is open for debate but I use this method for 99% of all my code with no trouble at all.
I moved to ASP.NET from PHP where the queries are run directly. So I always create Connection in the Page_Load Event, dispose it after I do all stuff needed, and access data with NpgsqlCommand. (Yes, I use Postgresql in my ASP.NET applications.)
After starting to learn ASP.NET MVC I was amazed how easy it is to access SQL with the LINQ to SQL thing. But... It works only with MS SQL. So my question is how to implement the same functionality in my applications? How to connect to databases easily?
I wrote my own wrapper classes for connecting to Postgresql. 1 class per a table.
This is a part of the Student class:
public class Student : User
{
private static NpgsqlConnection connection = null;
private const string TABLE_NAME = "students";
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
/// <summary>
/// Updates the student
/// </summary>
public void Update()
{
Connect();
Run(String.Format("UPDATE " + TABLE_NAME + " SET first_name='{0}', last_name='{1}', password='{2}' WHERE id={3}", FirstName, LastName, Password, Id));
connection.Dispose();
}
/// <summary>
/// Inserts a new student
/// </summary>
public void Insert()
{
Connect();
Run(String.Format("INSERT INTO " + TABLE_NAME + " (first_name, last_name, password) VALUES ('{0}', '{1}', '{2}')",FirstName, LastName, Password));
connection.Dispose();
}
private static void Run(string queryString)
{
NpgsqlCommand cmd = new NpgsqlCommand(queryString, connection);
cmd.ExecuteScalar();
cmd.Dispose();
}
private static void Connect()
{
connection = new NpgsqlConnection(String.Format("Server=localhost;Database=db;Uid=uid;Password=pass;pooling=false"));
connection.Open();
}
//....
So as you see the problem here is that with every INSERT, DELETE, UPDATE request I'm using Connect() method which connects to the database. I didn't realize how stupid it was before I had to wait for 10 minutes to have 500 rows inserted, as there were 500 connections to the database.
Using pooling while connecting does help, but still making the connection and making the server check the pool during every single query is stupid.
So I decided to move Connection property to a static DB class, and it didn't work either, because it's a really bad idea to store such objects as connections in a static class.
I really don't know what to do know. Yes, there's an option of manullay creating the connections in every Page_Load event and close them in the end like I'm doing it right now.
Student student = new Student { FirstName="Bob", LastName="Black" };
NpgsqlConnection connection = ... ;
student.Insert(connection);
But this code is pretty ugly. I will be really thankful to somebody who can hep me here.
I would not recommend this design. It is better to encapsulate each database call which means each call opens a new connection each time you need to do something on the db. This might sound inefficient if it were not for connection pooling. ASP.NET will reuse connections automatically for you in a pool. The problem in your design is that there is nothing that guarantees that the connection will be closed.
Thus, you should try something like
private static void Insert()
{
var sql = "Insert Into "....;
ExecuteActionQuery(sql);
}
private static void ExecuteActionQuery( string query )
{
using (var conn = new NpgsqlConnection(String.Format(connString))
{
conn.Open();
using ( var cmd = new NpgsqlCommand(query, connection) )
{
cmd.ExecuteNonQuery();
}
}
}
I typically make a few global functions that encapsulate the standard operations so that I need only pass a query and parameters and my method does the rest. In my example, my ExecuteActionQuery does not take parameters but this was for demonstration only.
Not really pertaining to your question but another solution, if you like linq to sql, you could try DBLinq which provides a Linq to SQL provider for Postgresql and others databases.
http://code.google.com/p/dblinq2007/