Does MYSQL support TransactionScope without passing the MySqlConnection object as a parameter?
Or does it just work with MS SQL 2008 and above?
for example:
public void Method()
{
using (var scope = new System.Transactions.TransactionScope())
{
Delete();
Insert();
Update();
scope.Complete();
}
}
public void Update()
{
using(MySqlConnection conn = new MySqlConnection())
{
// Update something in the Database
}
}
public void Insert()
{
using(MySqlConnection conn = new MySqlConnection())
{
// Insert something in the Database
}
}
public void Delete()
{
using(MySqlConnection conn = new MySqlConnection())
{
// Delete something from Database
}
}
or should I do it with the MySqlConnection conn object as a parameter?
public void Method()
{
using (var scope = new System.Transactions.TransactionScope())
{
using(MySqlConnection conn = new MySqlConnection())
{
Delete(conn);
Insert(conn);
Update(conn);
}
scope.Complete();
}
}
public void Update(MysqlConnection conn)
{
// Update something in the Database
}
public void Insert(MysqlConnection conn)
{
// Insert something in the Database
}
public void Delete(MysqlConnection conn)
{
// Delete something from Database
}
You cannot create multiple MySqlConnection objects within a single TransactionScope: bug 50773.
A solution is to upgrade to https://github.com/mysql-net/MySqlConnector which does support TransactionScope.
Related
I'm using the following class to wrap my CRUD operations:
public class DAL
{
private MySqlConnection conn;
private MySqlCommand cmd = new MySqlCommand();
private MySqlDataAdapter adap = new MySqlDataAdapter();
private MySqlDataReader dr;
public MySqlDataReader Dr
{
get { return dr; }
set { dr = value; }
}
public MySqlDataAdapter Adap
{
get { return adap; }
set { adap = value; }
}
public MySqlCommand Cmd
{
get { return cmd; }
set { cmd = value; }
}
public MySqlConnection Conn
{
get { return conn; }
set { conn = value; }
}
public DAL(IConfiguration configuration)
{
this.conn = new MySqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public DAL(string constr)
{
this.conn = new MySqlConnection(constr);
}
}
Some professionals here say that my DAL Class is terrible because of this
To be honest I didn't understand it very well. But no problem.
You say it's not right to wrap Ado.net. So I don't do it anymore.
There is just one problem. What happens if I have to change my db from MySql to MSSQL?
How can I write code the way I can change my DB with changing just one file and not more.
How should I do that?
You should use interfaces.
By example instead of returning "MySqlDbConnection", you could return a "IDbConnection".
In that case the caller will only know it's a database connection. No matter if it's a MySQL or SQL Server connection.
That way the only file that knows about MySql specifically will be your DAL class.
Those interfaces already exist (IDbConnection, IDbCommand, ...) so don't need to create it yourself.
Edit :
In the following example I modified a few things :
Use interfaces (or abstract classes)
Using naming guidelines + readonly keyword
Use full name for naming things (as suggested by #pinkfloydx33 in a comment) it will help readibility. For example the name of the parameter you used for the connection string was "constr" wich, for me, was meaning "constructor" at first (without reading the following line).
What is not fixed:
The usage of the DbConnection (as you mentionned in your post, you should read again the question you linked because if it's a production code it can/will cause so issues).
Possible improvements:
Using an ORM or a library like Dapper (as suggested by #insane_developer in a comment)
public class DAL
{
private readonly IDbConnection _connection;
private DbCommand _command = new MySqlCommand();
private IDbDataAdapter = new MySqlDataAdapter();
private IDataReader _dataReader;
public IDataReader DataReader
{
get { return _dataReader; }
set { _dataReader = value; }
}
public IDbDataAdapter Adapter
{
get { return _adapter; }
set { _adapter = value; }
}
public DbCommand Cmd
{
get { return _command; }
set { _command = value; }
}
public IDbConnection Connection
{
get { return _connection; }
set { _connection = value; }
}
public DAL(IConfiguration configuration)
{
this._connection = new MySqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public DAL(string connectionString)
{
this._connection = new MySqlConnection(connectionString);
}
}
That way only this class knows about the DBMS type you're using.
i am new with MVC Core, i working on news website Project, how i can display last SQL record on my index?
please describe in details, what should i write in controller and razor page ?
thank you
public class mycontroller:BaseController
{
private readonly IRepo repo;
public mycontroller(IRepo _repo)
{
_repo=repo;
}
[httpGet]
public IActionResult<string> GetLastRecord()
{
return _repo.GetLastRecord();
}
}
public class repo
{
private readonly IDBContextFactory dBContextFactory;
public repo(IDBContextFactory _dbContextFactory)
{
_dbContextFactory=dBContextFactory;
}
public string GetLastRecord()
{
return _dbContextFactory.Select("mydb","select top 1 text from mydb order by autoincreamentedkeyusualyid desc")[0];/* this is bad way of using data table I recommend using this https://stackoverflow.com/questions/33515552/converting-datatable-to-listentity-projectdracula */;
}
}
public interface IRepo
{
public string GetLastRecord();
}
public class DBContextFactory
{
private SqlCommand BuildFactory(string dbName)
{
switch(dbName)
{
case 'mydb':
return CreateMyDB();
}
}
private SqlCommand CreateMyDB()
{
string connectionString = "your connection string";
SqlConnection connection =
new SqlConnection(connectionString));
SqlCommand command = new SqlCommand(connection);
return command.Open();
}
//Private SqlCommand GetMyOpenCommand()
public DataTable Select(string dbName,string query)
{
SqlDataAdapter dataAdapter=new SqlDataAdapter();
dataAdapter.SelectCommand=BuildFactory(dbName);
DataSet dataSet=new DataSet();
dataAdapter.Fill(dataSet);
con.Close();
}
}
public inteface IDBContextFactory
{
SqlCommand BuildFactory(string dbName);
SqlCommand CreateMyDB();
DataTable Select(string dbName,string query)
im writing database related tests, but they take long time to complete and im trying to speed them up..
my current code looks like this:
[TestClass]
public class TestClass
{
private const string ConnectionString = "...";
private const string TemplateDatabaseBackupPath = "...";
[ClassInitialize]
public static void ClassInitialize(TestContext context) {
InitializeTestEnviroment(); // create & fill db, init internals
using (SqlConnection connection = new SqlConnection(ConnectionString)) {
connection.Open();
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = "BACKUP DATABASE [Testing] TO DISK = #path";
command.Parameters.AddWithValue("path", TemplateDatabaseBackupPath);
command.ExecuteNonQuery();
}
}
}
[TestCleanup]
public static void TestCleanup() {
using (SqlConnection connection = new SqlConnection(ConnectionString)) {
connection.Open();
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = "ALTER DATABASE [Testing] SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
command.ExecuteNonQuery();
}
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = #"RESTORE DATABASE [Testing] FROM DISK = #path WITH REPLACE";
command.Parameters.AddWithValue("path", TemplateDatabaseBackupPath);
command.ExecuteNonQuery();
}
}
}
[TestMethod]
public void TestMethod() {
Entity entity = new Entity { Name = "Fred Flindstone" };
BlackBox.UpdateEntity(entity, 42);
Entity actual = BlackBox.GetEntityById(42);
Assert.AreEqual("Fred Flindstone", actual.Name);
}
[TestMethod]
public void TestMethod2() {
Entity entity = new Entity { Name = "Fred Flindstone" };
BlackBox.UpdateEntity(entity, 42); // 42 = entity id
Entity actual = BlackBox.GetEntityById(42);
Assert.AreEqual("Fred Flindstone", actual.Name);
}
}
// NH mapping class
public class Entity {
public int Id;
public string Name;
}
// Not real code - just to get the idea how CRUD are performed internally, this code cannot be easily changed (no unit tests for it)
public static class BlackBox {
public static void UpdateEntity(object entity, int id) {
using(var s = GetSession()){
s.Update(id, entity);
}
}
public static void GetEntityById(int id) {
using(var s = GetSession()){
return s.Session.Query<Entity>().Single(o => o.Id == Id);
}
}
private static IHiberSession GetSession(){
return ...; // not relevant
}
public interface IHiberSession {
ISession Session { get; }
...
}
}
after some mesumenents i found this:
ClassInitialize 8s
TestMethod and TestMethod2 ~20ms
TestCleanup 3s
obviously i would like to reduce TestCleanup time ...
after some searching i found System.Transactions.TransactionScope and thought i have a winner:
[ClassInitialize]
public static void ClassInitialize(TestContext context) {
InitializeTestEnviroment();
}
[TestInitialize]
public static void TestInitialize() {
_Scope = new System.Transactions.TransactionScope();
}
[TestCleanup]
public static void TestCleanup() {
_Scope.Dispose(); // Rollback any changes made by test
}
with should be faster than database restore but unfortunately
i get this exception during in session Close() method
System.InvalidOperationException: Disconnect cannot be called while a transaction is in progress.
with seems to be related to this closed issue: NH-1744 but that dont give me any idea, gow to fix my code :(
Anyway my question is how to make this code to work:
void TestMethod() {
using(var scope = new System.Transactions.TransactionScope()) {
// these two usings cannot be changed ...
using (ISession session = GetSession()) {
// some CRUD
}
using (ISession session = GetSession()) {
// some more CRUD
}
}
}
or any other idea, how to make database fast backup/restore? (in-memory backup would be nice)
PS: TransactionScope is used ONLY in unit testing, 'live' code is scopeless
Thanks for any help ...
I have a console application with a base class as following:
public abstract class PaymentSystemBase : IPayable
{
private SqlConnection _connection;
protected PaymentSystemBase()
{
CreateDatabaseConnection();
}
protected void CreateDatabaseConnection()
{
if(_connection == null)
{
string connectionString = ConfigurationManager.AppSettings["connString"];
var connection = new SqlConnection(connectionString);
_connection = connection;
connection.Open();
}
}
public SqlConnection Connection
{
get { return _connection; }
}
public abstract void ProcessPayment();
}
And have a few classes that derive from PaymentSystemBase:
public class PS1 : PaymentSystemBase
{
public override void ProcessPayment()
{
// Work with database using Connection from PaymentSystemBase
}
}
public class PS2 : PaymentSystemBase
{
public override void ProcessPayment()
{
// Work with database using Connection from PaymentSystemBase
}
}
In main program:
var lstPayments = new List<IPayable>
{
new PS1(),
new PS2()
};
var processPayments = new ProcessPayments(lstPayments);
processPayments.Process();
Where:
public class ProcessPayments
{
private List<IPayable> _paymentSystems;
public ProcessPayments(List<IPayable> paymentSystem)
{
_paymentSystems = paymentSystem;
}
public void Process()
{
foreach (var paymentSystem in _paymentSystems)
{
paymentSystem.ProcessPayment();
}
}
}
My question is how to use the same connection from PaymentSystemBase class and close it after processing? As I can see the connection was created again every time when PS1 and PS2 were created.
You shouldn't try to share the connection object. The connection objects themselves are actually quite lightweight, being an abstraction built on top of the actual physical connections, that the ADO.NET connection pool takes care of creating.
So you base class should be something like:
public abstract class PaymentSystemBase : IPayable
{
private static string _connectionString =
ConfigurationManager.ConnectionStrings["connString"].ConnectionString
public static string ConnectionString
{
get { return _connection; }
}
public abstract void ProcessPayment();
}
And then your derived classes should be:
public class PS1 : PaymentSystemBase
{
public override void ProcessPayment()
{
using(var conn = new SqlConnection(PaymentSystemBase.ConnectionString))
{
using(var cmd = new SqlCommand("...",conn)
{
//Prepare command
conn.Open();
cmd.ExecuteXXX();
//Process results, etc
}
}
}
}
You'll notice that I've also switched where the connection string is loaded from via the ConfigurationManager class from AppSettings to ConnectionStrings, which is a dedicated part of the configuration system for storing connection strings. This wasn't actually required but it is more conventional.
I have been trying to write an sql connection class for a small project,which will use simple sql commands for insert/update/select/delete ops and sometimes with transactions,is there any guidelines i could use? The class could be instantiated at any point with or without transactions...
Tried:
public class DbConnection
{
public static string srConnectionString = "blablab";
public DbConnection()
{
}
public static DataSet db_Select_Query(string strQuery)
{
DataSet dataSet = new DataSet();
try
{
using (SqlConnection connection = new SqlConnection(srConnectionString))
{
connection.Open();
SqlDataAdapter _SqlDataAdapter = new SqlDataAdapter(strQuery, connection);
DA.Fill(dataSet);
}
return dataSet;
}
catch (Exception)
{
//some error handling.
}
}
public static void db_Update_Delete_Query(string strQuery)
{
try
{
using (SqlConnection connection = new SqlConnection(srConnectionString))
{
connection.Open();
SqlCommand command = new SqlCommand(strQuery, connection);
command.ExecuteNonQuery();
}
}
catch (Exception)
{
//some error handling.
}
}
For example how can i add a parameter that opens or closes transaction as long as this class is used?For example i could be able to call db.commit or db.rollback from outside the class.
PS:tried some micro orms(petapoco for ex.) ,is there a way to run them with pure sql and get return type as datasets or datatables only?
Regards.
Edit:
public class dbconnection : IDisposable
{
public static string strConnectionString = #"Data Source=PC137\PC137_SQL2008;Initial Catalog=BARZO;Integrated Security=True";
#region IDisposable Members
public void Dispose()
{
GC.SuppressFinalize(this);
}
#endregion
private SqlTransaction transaction;
private SqlConnection connection;
private SqlCommand command = new SqlCommand();
public void db_OpenConnection(bool WithTransaction)
{
connection = new SqlConnection(strConnectionString);
connection.Open();
if (WithTransaction)
{
transaction = connection.BeginTransaction();
}
}
public void db_CloseConnection()
{
connection.Close();
connection.Dispose();
transaction.Dispose();
command.Dispose();
}
public void db_Commit()
{
transaction.Commit();
}
public void db_RollBack()
{
transaction.Rollback();
}
public DataSet db_Select_Query(string strQuery)
{
var dataSet = new DataSet();
try
{
SqlDataAdapter SqlDataAdapter = new SqlDataAdapter(strQuery, connection);
SqlDataAdapter.Fill(dataSet);
return dataSet;
}
catch (SqlException sqlError)
{
MessageBox.Show(sqlError,MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
public bool db_Update_Delete_Query(string strQuery)
{
try
{
command = new SqlCommand(strQuery, connection);
command.Transaction = transaction;
command.ExecuteNonQuery();
}
catch (SqlException sqlError)
{
MessageBox.Show(sqlError,MessageBoxButtons.OK, MessageBoxIcon.Stop);
return false;
}
return true;
}
}
I would make the db_Update_Delete_Query() method return your own DbCommandToken, which wraps an SqlTransaction object, and can be used to cancel the transaction or report the completion of the command.
This would look like this.
public class DbConnection
{
public static DbCommandToken db_Update_Delete_Query(string strQuery)
{
try
{
using (SqlConnection connection = new SqlConnection(strConnectionString))
{
connection.Open();
var transaction = connection.BeginTransaction();
SqlCommand command = new SqlCommand(strQuery, connection);
return new DbCommandToken(transaction, command);
}
}
catch (Exception)
{
//some error handling.
}
}
}
Note that both your command and your transaction are now owned by your DbCommandToken object, which should implement IDisposable.
If your command is a long-running command, which you may want to run asynchronously, and maybe cancel with a later request, you can add success and failure callbacks to your DbCommandToken: something along these lines.
public class DbCommandToken : IDisposable
{
private readonly SqlTransaction _transaction;
private readonly SqlCommand _command;
public DbCommandToken(SqlTransaction transaction, SqlCommand command)
{
_transaction = transaction;
_command = command;
}
public Action Success { get; set; }
public Action Failure { get; set; }
public Task<int> Execute()
{
return Task.Factory.StartNew(() => _command.ExecuteNonQuery())
.ContinueWith(t =>
{
var rowsAffected = t.Result;
if (rowsAffected >= 0)
{
_transaction.Commit();
Success();
}
...Handle other scenarios here...
return t.Result;
});
}
public void Cancel()
{
_transaction.Rollback();
}
public void Dispose()
{
_command.Dispose();
_transaction.Dispose();
}
}