I have a csharp app with a xml config file containing an element called "environment" which can be set to different values (development/test/production for example).
When this config file entry is modified the resulting global variables within the application should all change. I have a class in my application called Globals were I am storing global variables. I want to add a case/switch element to it but it won't seem to work.
So for example I have the following defined at the top of the globals class:
public static string environment = MyApp.Properties.Settings.Default.Environment;
Then lower down in my class I'm trying to do the following:
switch (environment)
{
case "development":
public static string Server = "SQL1";
public static string Username = "dev.user";
case "test":
public static string Server = "SQL2";
public static string Username = "test.user";
case "production":
public static string Server = "SQL3";
public static string Username = "prod.user";
default:
public static string Server = "SQL1";
public static string Username = "dev.user";
}
(In the example above I reduced the number of variables to two just to make it more understandable but in reality there are probably 30 variables per environment.)
This doesn't work I get multiple errors:
Invalid token 'switch' in class, struct, or interface member declaration
Invalid token ')' in class, struct, or interface member declaration
Invalid token 'case' in class, struct, or interface member declaration
Is there some other way of doing this?
Thanks
Brad
public static class Globals
{
public static string Environment = MyApp.Properties.Settings.Default.Environment;
public static string Server;
// rest of code
public static void LoadEnvironment()
{
switch (Environment)
{
case "development":
{
Server = "SQL1";
Username = "dev.user";
break;
}
// rest of code
}
}
}
Based on the error the compiler thinks it is coded inside the body of the class. Try moving the logic inside a method or some such and this may be due to your access modifiers inside your switch statement - eg. public static etc
public static should be declare in the class scope, not within a function.
You can initializes static variable either in the line of declaration or within the static constructor.
You forgot puting "break at the end of each case.
So the code should looke like this:
public class MyClass
{
public static string Server;
public static string Username;
static MyClass()
{
switch (environment)
{
case "development":
Server = "SQL1";
Username = "dev.user";
break;
case "test":
Server = "SQL2";
Username = "test.user";
break;
case "production":
Server = "SQL3";
Username = "prod.user";
break;
default:
Server = "SQL1";
Username = "dev.user";
break;
}
}
}
Try to define your strings before the switch statement. For each case, you have to define a break statement to make the pointer get out of the switch structure.
For your case, it's a good idea to define the Server and Username as properties and in a static constructor of a static class, define these values from the object. For sample:
public static class Globals
{
// define properties
public static string Server { get; set; }
public static string Username { get; set; }
// encapsulate the Settings.Environment in a property
public public static string Environment
{
get { return MyApp.Properties.Settings.Default.Environment; }
}
// when the application starts, this static scope will execute!
static Globals()
{
switch (environment)
{
case "development":
Server = "SQL1";
Username = "dev.user";
break;
case "test":
Server = "SQL2";
Username = "test.user";
break;
case "production":
Server = "SQL3";
Username = "prod.user";
break;
default:
Server = "SQL1";
Username = "dev.user";
}
}
}
To use it, just call
var server = Globals.Server;
var user = Globals.Username;
How can i sign-out and redirect to the login page from a public static class?
I have tried the following but it does not stop page execution..
public static DatabaseNameEntities CreateEntitiesForSpecificDatabaseName(bool contextOwnsConnection = true)
{
string database_name = "";
try
{
database_name = System.Web.HttpContext.Current.Application["DB_NAME"].ToString();
}
catch (NullReferenceException)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
//Initialize the SqlConnectionStringBuilder
//Initialize the EntityConnectionStringBuilder
//Create entity connection
EntityConnection connection = new EntityConnection(entityBuilder.ConnectionString);
return new DatabaseNameEntities(connection);
}
I have tried the following but it does not stop page execution..
That's because it's simply not the MVC way. It's also breaks the Single Responsibility Principle, that is, why would a method named CreateEntitiesForSpecificDatabaseName() know anything about MVC or logging out a user. The code you posted generally breaks this principle multiple times (application state, signing out a user).
Additionally, catching an exception you can prevent is also poor practice (or as the Lead Developer for the C# Compiler team called it, Boneheaded Exceptions).
Consider the following code.
public static ControllerBaseExtensions
{
private const string DBNAME = "DB_NAME";
public static bool TryGetDatabaseName(this ControllerBase instance,
out string DbName)
{
DbName = null;
var app = GetApp(instance);
var result = app.Any(k => k == DBNAME);
if (result)
{
DbName = instance.Application[DBNAME] as string;
result = DbName != null;
}
return result;
}
public static void SetDatabaseName(this ControllerBase instance,
string DbName)
{
var app = GetApp(instance);
app[DBNAME] = DbName;
}
private static HttpApplication GetApp(ControllerBase instance)
{
return instance.ControllerContext.HttpContext.Application;
}
}
public ActionResult MyMethod()
{
string DbName;
if (!this.TryGetDatabaseName(out DbName))
{
FormsAuthentication.SignOut();
// http://stackoverflow.com/questions/30509980
RedirectToAction("Login", "Account");
}
CreateEntitiesForSpecificDatabaseName(Dbname);
}
public static DatabaseNameEntities CreateEntitiesForSpecificDatabaseName(
string dbName,
bool contextOwnsConnection = true)
{
//Initialize the SqlConnectionStringBuilder
//Initialize the EntityConnectionStringBuilder
//Create entity connection
EntityConnection connection = new
EntityConnection(entityBuilder.ConnectionString);
return new DatabaseNameEntities(connection);
}
Does simply ie, no try or catch work as expected
public static DatabaseNameEntities CreateEntitiesForSpecificDatabaseName(bool contextOwnsConnection = true)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
In my ASP.NET C# website I am attempting to use Sessions to allow users to log in and navigate throughout the secure pages while the session is valid. If for whatever reason they timeout or sign out, they are to be redirected to the landing page. Currently the site only allows one user to be logged in at a time. It seems apparent that the session information is being stored incorrectly, but I don't understand where or why this occurs. If you access the page using another browser, you can see the code pulling information out of the session (like the username) that it should not know.
I want to allow multiple valid users to be logged in simultaneously and have no adverse affect on each other while doing so. If you need further information than the code samples I post below, please ask.
My Login page:
//Login.ascx.cs
...
private void Login_Click(object sender, EventArgs e)
{
if (sender == null || e == null)
{
throw new ArgumentNullException("Null Exception: Login_Click");
}
User user = new User();
user.Login(_username.Text, _password.Text);
if (user.IsValid() && user.GetIsUser() != false)
{
user.Save();
Session["Username"] = _username.Text;
Response.Redirect("Secure/Default.aspx");
}
else
{
DisplayErrors(user._validationErrors);
}
_errors.Text = errorMessage;
}
The welcome page (first secure page a user sees)
private void Page_Load(object sender, System.EventArgs e)
{
Business.User user = new Business.User();
_labelUsername.Text = MySession.Current.Username;
_labelCompanyId.Text = MySession.Current.CompanyId;
redirectLogin = "../Default.aspx";
//redirect if any conditions fail for user validation.
if (sessionKey != MySession.GetSessionId() || (string)Session["Username"] != _labelUsername.Text)
{
Response.Redirect(redirectLogin);
}
else
{
Debug.WriteLine("Welcome SUCCESS: " + _labelUsername.Text);
Debug.WriteLine("Welcome " + sessionKey);
}
}
And finally the User page that includes SQL Query
public static string dataUsername;
public static string dataCompanyId;
private const string _getUserByUsernameQuery =
#"SELECT [User].[username], [Company].[name]
FROM [User] WITH (NOLOCK) INNER JOIN [Company] WITH (NOLOCK)
ON [User].[companyId] = [Company].[id]
WHERE [User].[username] = #username AND [User].[password] = #password";
private string _username;
private string _companyid;
public User(){}
public void Login (string username, string password)
{
using (SqlConnection connection = new SqlConnection(SQLConfiguration.ConnectionString))
{
SqlCommand command = new SqlCommand(_getUserByUsernameQuery, connection);
command.Parameters.AddWithValue("#username", username);
command.Parameters.AddWithValue("#password", password);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Username = Convert.ToString(reader["username"]);
CompanyId = Convert.ToString(reader["name"]);
dataUsername = Username;
dataCompanyId = CompanyId;
}
}
}
}
#region Properties
public string Username
{
get{ return _username; }
set{ _username = value;}
}
public string CompanyId
{
get{ return _companyid;}
set{ _companyid = value;}
}
#endregion
EDIT: In response to some of the questions:
//in the first accessed page for secure users, before 'Page_load'
public static string sessionKey
{
get
{
return MySession.GetSessionId();
}
}
...
//in my 'MySession' class
public static string GetSessionId()
{
return System.Web.HttpContext.Current.Session.SessionID;
}
See Static Classes and Static Class Members (C# Programming Guide)
A static constructor is only called one time, and a static class remains in memory for the lifetime of the application domain in which your program resides.
The code-
public static string GetSessionId()
{
return System.Web.HttpContext.Current.Session.SessionID;
}
Will return the same SessionID for the lifetime of the application.
Like other posters I would strongly recommend you use the ASP.NET built in membership providers rather than try and invent your own, this will be more secure, more widely understood and thus supported, easier to maintain and more extendable.
Just use ASP.NET Forms Authentication. I bet all anonymous users are sharing the same session object. You can hook it into your own authentication scheme if something already exists. (passwords in a database or file, for instance)
having difficulty with a bit of code I have. I think variables are stuck inside the method I'm invoking and when I go to run that method again variables are no longer there.
Below is an example of my main:
public partial class Login : Form
{
public Login()
{
InitializeComponent();
}
sqlconnect sql = new sqlconnect();
public string pass;
public string user;
public void button1_Click(object sender, EventArgs e)
{
//Username and password textboxes send to public strings
user = textBox1.Text;
pass = textBox2.Text;
try
{
//try connecting to SQL database using SQL class
sql.GetSqlConnection();
sql.myConnection.Open();
this.Hide();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Here is the SQL connection class:
namespace NHS_Workshop_System
{
public class sqlconnect
{
public SqlConnection myConnection { get; set; }
Settings1 set = new Settings1();
Login var = new Login();
public SqlConnection GetSqlConnection()
{
if (myConnection == null)
myConnection = new SqlConnection("user id="+(var.user)+"; password="+(var.pass)+";server="+(set.SelectServer)+";Trusted_Connection="+(set.SelectContype)+";database="+(set.SelectDataBase)+"; connection timeout="+(set.Condrop)+"");
return myConnection;
}
}
}
So when I try to login it may work first time but if another form tries to run that method the username and password are missing. I'm not sure how to make the variables global so that every time I run the method it calls those details without fail. So the second time it uses sqlconnection on another form the ex message states that username and password are non existent. Is this a scoping issue if so can someone shed some light on how i might go about this maybe a new method of gaining access to an SQL server? Thank you tearing out my hair
Below is the answer for your question the way you have it now. But I strongly recommend you to think about what you are doing since your class logics doesn't seem to be very good. I see that your trying to implement Singleton which is good, but you can still make more instances of sqlconnect if you would like.
In your Login class, pass this instance of Login so the sqlconnector knows the user and pass that is declared here.
public partial class Login : Form
{
// Old style - sqlconnect sql = new sqlconnect();
sqlconnect sql;
public string pass; // These arent declared and wont be of use for your sql connect class
public string user;
public Login( String pass, String user )
{
this.pass = pass;
this.user = user;
// Now that we have a login with declared variables, pass it to the sqlconnect object
this.sql = new sqlconnect(this);
InitializeComponent();
}
Now in your sqlconnect class, before your Login variable was just empty. Now you have received an instance of it in your constructor that has it's variables set,
public class sqlconnect
{
public SqlConnection myConnection { get; set; }
Settings1 set = new Settings1();
// Here you maked a new one, use the existing instead from which this class is called.
// changed variable named from var to login..
Login login;
// *NEW* Constructor where you pass your login object
public sqlconnect(Login login)
{
this.login = login;
}
public SqlConnection GetSqlConnection()
{
if (myConnection == null)
myConnection = new SqlConnection("user id="+(login.user)+"; password="+(login.pass)+";server="+(set.SelectServer)+";Trusted_Connection="+(set.SelectContype)+";database="+(set.SelectDataBase)+"; connection timeout="+(set.Condrop)+"");
return myConnection;
}
}
I've been playing with Dapper, but I'm not sure of the best way to handle the database connection.
Most examples show the connection object being created in the example class, or even in each method. But it feels wrong to me to reference a connection string in every clss, even if it's pulling from the web.config.
My experience has been with using a DbDataContext or DbContext with Linq to SQL or Entity Framework, so this is new to me.
How do I structure my web apps when using Dapper as my Data Access strategy?
Update: clarification from MarredCheese's comment:
"No need to use a using statement. Dapper will automatically open,
close, and dispose of the connection for you." That's not correct.
Dapper will automatically open closed connections, and it will
automatically close connections that it auto-opened, but it will not
automatically dispose of connections. Marc Gravell and Eric Lippert
both advocate using using with Dapper here.
Microsoft.AspNetCore.All: v2.0.3 | Dapper: v1.50.2
I am not sure if I am using the best practices correctly or not, but I am doing it this way, in order to handle multiple connection strings.
It's easy if you have only 1 connection string
Startup.cs
using System.Data;
using System.Data.SqlClient;
namespace DL.SO.Project.Web.UI
{
public class Startup
{
public IConfiguration Configuration { get; private set; }
// ......
public void ConfigureServices(IServiceCollection services)
{
// Read the connection string from appsettings.
string dbConnectionString = this.Configuration.GetConnectionString("dbConnection1");
// Inject IDbConnection, with implementation from SqlConnection class.
services.AddTransient<IDbConnection>((sp) => new SqlConnection(dbConnectionString));
// Register your regular repositories
services.AddScoped<IDiameterRepository, DiameterRepository>();
// ......
}
}
}
DiameterRepository.cs
using Dapper;
using System.Data;
namespace DL.SO.Project.Persistence.Dapper.Repositories
{
public class DiameterRepository : IDiameterRepository
{
private readonly IDbConnection _dbConnection;
public DiameterRepository(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}
public IEnumerable<Diameter> GetAll()
{
const string sql = #"SELECT * FROM TABLE";
// No need to use using statement. Dapper will automatically
// open, close and dispose the connection for you.
return _dbConnection.Query<Diameter>(sql);
}
// ......
}
}
Problems if you have more than 1 connection string
Since Dapper utilizes IDbConnection, you need to think of a way to differentiate different database connections.
I tried to create multiple interfaces, 'inherited' from IDbConnection, corresponding to different database connections, and inject SqlConnection with different database connection strings on Startup.
That failed because SqlConnection inherits from DbConnection, and DbConnection inplements not only IDbConnection but also Component class. So your custom interfaces won't be able to use just the SqlConnection implenentation.
I also tried to create my own DbConnection class that takes different connection string. That's too complicated because you have to implement all the methods from DbConnection class. You lost the help from SqlConnection.
What I end up doing
During Startup, I loaded all connection string values into a dictionary. I also created an enum for all the database connection names to avoid magic strings.
I injected the dictionary as Singleton.
Instead of injecting IDbConnection, I created IDbConnectionFactory and injected that as Transient for all repositories. Now all repositories take IDbConnectionFactory instead of IDbConnection.
When to pick the right connection? In the constructor of all repositories! To make things clean, I created repository base classes and have the repositories inherit from the base classes. The right connection string selection can happen in the base classes.
DatabaseConnectionName.cs
namespace DL.SO.Project.Domain.Repositories
{
public enum DatabaseConnectionName
{
Connection1,
Connection2
}
}
IDbConnectionFactory.cs
using System.Data;
namespace DL.SO.Project.Domain.Repositories
{
public interface IDbConnectionFactory
{
IDbConnection CreateDbConnection(DatabaseConnectionName connectionName);
}
}
DapperDbConenctionFactory - my own factory implementation
namespace DL.SO.Project.Persistence.Dapper
{
public class DapperDbConnectionFactory : IDbConnectionFactory
{
private readonly IDictionary<DatabaseConnectionName, string> _connectionDict;
public DapperDbConnectionFactory(IDictionary<DatabaseConnectionName, string> connectionDict)
{
_connectionDict = connectionDict;
}
public IDbConnection CreateDbConnection(DatabaseConnectionName connectionName)
{
string connectionString = null;
if (_connectDict.TryGetValue(connectionName, out connectionString))
{
return new SqlConnection(connectionString);
}
throw new ArgumentNullException();
}
}
}
Startup.cs
namespace DL.SO.Project.Web.UI
{
public class Startup
{
// ......
public void ConfigureServices(IServiceCollection services)
{
var connectionDict = new Dictionary<DatabaseConnectionName, string>
{
{ DatabaseConnectionName.Connection1, this.Configuration.GetConnectionString("dbConnection1") },
{ DatabaseConnectionName.Connection2, this.Configuration.GetConnectionString("dbConnection2") }
};
// Inject this dict
services.AddSingleton<IDictionary<DatabaseConnectionName, string>>(connectionDict);
// Inject the factory
services.AddTransient<IDbConnectionFactory, DapperDbConnectionFactory>();
// Register your regular repositories
services.AddScoped<IDiameterRepository, DiameterRepository>();
// ......
}
}
}
DiameterRepository.cs
using Dapper;
using System.Data;
namespace DL.SO.Project.Persistence.Dapper.Repositories
{
// Move the responsibility of picking the right connection string
// into an abstract base class so that I don't have to duplicate
// the right connection selection code in each repository.
public class DiameterRepository : DbConnection1RepositoryBase, IDiameterRepository
{
public DiameterRepository(IDbConnectionFactory dbConnectionFactory)
: base(dbConnectionFactory) { }
public IEnumerable<Diameter> GetAll()
{
const string sql = #"SELECT * FROM TABLE";
// No need to use using statement. Dapper will automatically
// open, close and dispose the connection for you.
return base.DbConnection.Query<Diameter>(sql);
}
// ......
}
}
DbConnection1RepositoryBase.cs
using System.Data;
using DL.SO.Project.Domain.Repositories;
namespace DL.SO.Project.Persistence.Dapper
{
public abstract class DbConnection1RepositoryBase
{
public IDbConnection DbConnection { get; private set; }
public DbConnection1RepositoryBase(IDbConnectionFactory dbConnectionFactory)
{
// Now it's the time to pick the right connection string!
// Enum is used. No magic string!
this.DbConnection = dbConnectionFactory.CreateDbConnection(DatabaseConnectionName.Connection1);
}
}
}
Then for other repositories that need to talk to the other connections, you can create a different repository base class for them.
using System.Data;
using DL.SO.Project.Domain.Repositories;
namespace DL.SO.Project.Persistence.Dapper
{
public abstract class DbConnection2RepositoryBase
{
public IDbConnection DbConnection { get; private set; }
public DbConnection2RepositoryBase(IDbConnectionFactory dbConnectionFactory)
{
this.DbConnection = dbConnectionFactory.CreateDbConnection(DatabaseConnectionName.Connection2);
}
}
}
using Dapper;
using System.Data;
namespace DL.SO.Project.Persistence.Dapper.Repositories
{
public class ParameterRepository : DbConnection2RepositoryBase, IParameterRepository
{
public ParameterRepository (IDbConnectionFactory dbConnectionFactory)
: base(dbConnectionFactory) { }
public IEnumerable<Parameter> GetAll()
{
const string sql = #"SELECT * FROM TABLE";
return base.DbConnection.Query<Parameter>(sql);
}
// ......
}
}
Hope all these help.
It was asked about 4 years ago... but anyway, maybe the answer will be useful to someone here:
I do it like this in all the projects.
First, I create a base class which contains a few helper methods like this:
public class BaseRepository
{
protected T QueryFirstOrDefault<T>(string sql, object parameters = null)
{
using (var connection = CreateConnection())
{
return connection.QueryFirstOrDefault<T>(sql, parameters);
}
}
protected List<T> Query<T>(string sql, object parameters = null)
{
using (var connection = CreateConnection())
{
return connection.Query<T>(sql, parameters).ToList();
}
}
protected int Execute(string sql, object parameters = null)
{
using (var connection = CreateConnection())
{
return connection.Execute(sql, parameters);
}
}
// Other Helpers...
private IDbConnection CreateConnection()
{
var connection = new SqlConnection(...);
// Properly initialize your connection here.
return connection;
}
}
And having such a base class I can easily create real repositories without any boilerplate code:
public class AccountsRepository : BaseRepository
{
public Account GetById(int id)
{
return QueryFirstOrDefault<Account>("SELECT * FROM Accounts WHERE Id = #Id", new { id });
}
public List<Account> GetAll()
{
return Query<Account>("SELECT * FROM Accounts ORDER BY Name");
}
// Other methods...
}
So all the code related to Dapper, SqlConnection-s and other database access stuff is located in one place (BaseRepository). All real repositories are clean and simple 1-line methods.
I hope it will help someone.
I created extension methods with a property that retrieves the connection string from configuration. This lets the callers not have to know anything about the connection, whether it's open or closed, etc. This method does limit you a bit since you're hiding some of the Dapper functionality, but in our fairly simple app it's worked fine for us, and if we needed more functionality from Dapper we could always add a new extension method that exposes it.
internal static string ConnectionString = new Configuration().ConnectionString;
internal static IEnumerable<T> Query<T>(string sql, object param = null)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
return conn.Query<T>(sql, param);
}
}
internal static int Execute(string sql, object param = null)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
return conn.Execute(sql, param);
}
}
I do it like this:
internal class Repository : IRepository {
private readonly Func<IDbConnection> _connectionFactory;
public Repository(Func<IDbConnection> connectionFactory)
{
_connectionFactory = connectionFactory;
}
public IWidget Get(string key) {
using(var conn = _connectionFactory())
{
return conn.Query<Widget>(
"select * from widgets with(nolock) where widgetkey=#WidgetKey", new { WidgetKey=key });
}
}
}
Then, wherever I wire-up my dependencies (ex: Global.asax.cs or Startup.cs), I do something like:
var connectionFactory = new Func<IDbConnection>(() => {
var conn = new SqlConnection(
ConfigurationManager.ConnectionStrings["connectionString-name"];
conn.Open();
return conn;
});
Best practice is a real loaded term. I like a DbDataContext style container like Dapper.Rainbow promotes. It allows you to couple the CommandTimeout, transaction and other helpers.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Dapper;
// to have a play, install Dapper.Rainbow from nuget
namespace TestDapper
{
class Program
{
// no decorations, base class, attributes, etc
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? LastPurchase { get; set; }
}
// container with all the tables
class MyDatabase : Database<MyDatabase>
{
public Table<Product> Products { get; set; }
}
static void Main(string[] args)
{
var cnn = new SqlConnection("Data Source=.;Initial Catalog=tempdb;Integrated Security=True");
cnn.Open();
var db = MyDatabase.Init(cnn, commandTimeout: 2);
try
{
db.Execute("waitfor delay '00:00:03'");
}
catch (Exception)
{
Console.WriteLine("yeah ... it timed out");
}
db.Execute("if object_id('Products') is not null drop table Products");
db.Execute(#"create table Products (
Id int identity(1,1) primary key,
Name varchar(20),
Description varchar(max),
LastPurchase datetime)");
int? productId = db.Products.Insert(new {Name="Hello", Description="Nothing" });
var product = db.Products.Get((int)productId);
product.Description = "untracked change";
// snapshotter tracks which fields change on the object
var s = Snapshotter.Start(product);
product.LastPurchase = DateTime.UtcNow;
product.Name += " World";
// run: update Products set LastPurchase = #utcNow, Name = #name where Id = #id
// note, this does not touch untracked columns
db.Products.Update(product.Id, s.Diff());
// reload
product = db.Products.Get(product.Id);
Console.WriteLine("id: {0} name: {1} desc: {2} last {3}", product.Id, product.Name, product.Description, product.LastPurchase);
// id: 1 name: Hello World desc: Nothing last 12/01/2012 5:49:34 AM
Console.WriteLine("deleted: {0}", db.Products.Delete(product.Id));
// deleted: True
Console.ReadKey();
}
}
}
Try this:
public class ConnectionProvider
{
DbConnection conn;
string connectionString;
DbProviderFactory factory;
// Constructor that retrieves the connectionString from the config file
public ConnectionProvider()
{
this.connectionString = ConfigurationManager.ConnectionStrings[0].ConnectionString.ToString();
factory = DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings[0].ProviderName.ToString());
}
// Constructor that accepts the connectionString and Database ProviderName i.e SQL or Oracle
public ConnectionProvider(string connectionString, string connectionProviderName)
{
this.connectionString = connectionString;
factory = DbProviderFactories.GetFactory(connectionProviderName);
}
// Only inherited classes can call this.
public DbConnection GetOpenConnection()
{
conn = factory.CreateConnection();
conn.ConnectionString = this.connectionString;
conn.Open();
return conn;
}
}
Everyone appears to be opening their connections entirely too early? I had this same question, and after digging through the Source here - https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs
You will find that every interaction with the database checks the connection to see if it is closed, and opens it as necessary. Due to this, we simply utilize using statements like above without the conn.open(). This way the connection is opened as close to the interaction as possible. If you notice, it also immediately closes the connection. This will also be quicker than it closing automatically during disposal.
One of the many examples of this from the repo above:
private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition command, Action<IDbCommand, object> paramReader)
{
IDbCommand cmd = null;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
cmd = command.SetupCommand(cnn, paramReader);
if (wasClosed) cnn.Open();
int result = cmd.ExecuteNonQuery();
command.OnCompleted();
return result;
}
finally
{
if (wasClosed) cnn.Close();
cmd?.Dispose();
}
}
Below is a small example of how we use a Wrapper for Dapper called the DapperWrapper. This allows us to wrap all of the Dapper and Simple Crud methods to manage connections, provide security, logging, etc.
public class DapperWrapper : IDapperWrapper
{
public IEnumerable<T> Query<T>(string query, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
using (var conn = Db.NewConnection())
{
var results = conn.Query<T>(query, param, transaction, buffered, commandTimeout, commandType);
// Do whatever you want with the results here
// Such as Security, Logging, Etc.
return results;
}
}
}
I wrap connection with the helper class:
public class ConnectionFactory
{
private readonly string _connectionName;
public ConnectionFactory(string connectionName)
{
_connectionName = connectionName;
}
public IDbConnection NewConnection() => new SqlConnection(_connectionName);
#region Connection Scopes
public TResult Scope<TResult>(Func<IDbConnection, TResult> func)
{
using (var connection = NewConnection())
{
connection.Open();
return func(connection);
}
}
public async Task<TResult> ScopeAsync<TResult>(Func<IDbConnection, Task<TResult>> funcAsync)
{
using (var connection = NewConnection())
{
connection.Open();
return await funcAsync(connection);
}
}
public void Scope(Action<IDbConnection> func)
{
using (var connection = NewConnection())
{
connection.Open();
func(connection);
}
}
public async Task ScopeAsync<TResult>(Func<IDbConnection, Task> funcAsync)
{
using (var connection = NewConnection())
{
connection.Open();
await funcAsync(connection);
}
}
#endregion Connection Scopes
}
Examples of usage:
public class PostsService
{
protected IConnectionFactory Connection;
// Initialization here ..
public async Task TestPosts_Async()
{
// Normal way..
var posts = Connection.Scope(cnn =>
{
var state = PostState.Active;
return cnn.Query<Post>("SELECT * FROM [Posts] WHERE [State] = #state;", new { state });
});
// Async way..
posts = await Connection.ScopeAsync(cnn =>
{
var state = PostState.Active;
return cnn.QueryAsync<Post>("SELECT * FROM [Posts] WHERE [State] = #state;", new { state });
});
}
}
So I don't have to explicitly open the connection every time.
Additionally, you can use it this way for the convenience' sake of the future refactoring:
var posts = Connection.Scope(cnn =>
{
var state = PostState.Active;
return cnn.Query<Post>($"SELECT * FROM [{TableName<Post>()}] WHERE [{nameof(Post.State)}] = #{nameof(state)};", new { state });
});
What is TableName<T>() can be found in this answer.
Hi #donaldhughes I'm new on it too, and I use to do this:
1 - Create a class to get my Connection String
2 - Call the connection string class in a Using
Look:
DapperConnection.cs
public class DapperConnection
{
public IDbConnection DapperCon {
get
{
return new SqlConnection(ConfigurationManager.ConnectionStrings["Default"].ToString());
}
}
}
DapperRepository.cs
public class DapperRepository : DapperConnection
{
public IEnumerable<TBMobileDetails> ListAllMobile()
{
using (IDbConnection con = DapperCon )
{
con.Open();
string query = "select * from Table";
return con.Query<TableEntity>(query);
}
}
}
And it works fine.