What is DbContext doing in ADO.NET - c#

I have code that make connection to the database and perform CRUD operations
Please see the code below:
We have in the code used "DbContext".
Is it generic and can be used with all kinds of databases or is it made for SQLServer and what is its purpose/mission?
I thought DBContext was only used with the Entity Framework
public class UserRepository : Repository<User>
{
private DbContext _context;
public UserRepository(DbContext context)
: base(context)
{
_context = context;
}
public IList<User> GetUsers()
{
using (var command = _context.CreateCommand())
{
command.CommandText = "exec [dbo].[uspGetUsers]";
return this.ToList(command).ToList();
}
}
public User CreateUser(User user)
{
using (var command = _context.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "uspSignUp";
command.Parameters.Add(command.CreateParameter("#pFirstName", user.FirstName));
command.Parameters.Add(command.CreateParameter("#pLastName", user.LastName));
command.Parameters.Add(command.CreateParameter("#pUserName", user.UserName));
command.Parameters.Add(command.CreateParameter("#pPassword", user.Password));
command.Parameters.Add(command.CreateParameter("#pEmail", user.Email));
return this.ToList(command).FirstOrDefault();
}
}
public User LoginUser(string id, string password)
{
using (var command = _context.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "uspSignIn";
command.Parameters.Add(command.CreateParameter("#pId", id));
command.Parameters.Add(command.CreateParameter("#pPassword", password));
return this.ToList(command).FirstOrDefault();
}
}
public User GetUserByUsernameOrEmail(string username, string email)
{
using (var command = _context.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "uspGetUserByUsernameOrEmail";
command.Parameters.Add(command.CreateParameter("#pUsername", username));
command.Parameters.Add(command.CreateParameter("#pEmail", email));
return this.ToList(command).FirstOrDefault();
}
}
}
Here is DbContext class:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DataAccessLayer
{
public class DbContext
{
private readonly IDbConnection _connection;
private readonly IConnectionFactory _connectionFactory;
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
private readonly LinkedList<AdoNetUnitOfWork> _uows = new LinkedList<AdoNetUnitOfWork>();
public DbContext(IConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
_connection = _connectionFactory.Create();
}
public IUnitOfWork CreateUnitOfWork()
{
var transaction = _connection.BeginTransaction();
var uow = new AdoNetUnitOfWork(transaction, RemoveTransaction, RemoveTransaction);
_rwLock.EnterWriteLock();
_uows.AddLast(uow);
_rwLock.ExitWriteLock();
return uow;
}
public IDbCommand CreateCommand()
{
var cmd = _connection.CreateCommand();
_rwLock.EnterReadLock();
if (_uows.Count > 0)
cmd.Transaction = _uows.First.Value.Transaction;
_rwLock.ExitReadLock();
return cmd;
}
private void RemoveTransaction(AdoNetUnitOfWork obj)
{
_rwLock.EnterWriteLock();
_uows.Remove(obj);
_rwLock.ExitWriteLock();
}
public void Dispose()
{
_connection.Dispose();
}
}
}

You can use EntityFramework to execute raw SQL, but looks like DbContext in your example is not the one from EntityFramework. It can be some other library or custom implementation in your project. You should be able to tell that by examining using imports, or by navigating to the DbContext definition.

I thought DBContext was only used with the Entity Framework
You're right, DBContext is a EntityFramework class that combines unit of work and repository pattern.
However, you seem to have a custom DBContext created using ado.net. Though, I wouldn't really call it a DBContext. Since normally DBContext follows ObjectContext concept of referring to a domain object i.e. within a context. This particular custom class is more like a DbCommand factory.
Is it generic and can be used with all kinds of databases or is it made for SQLServer[...]?
It's not SQL server specific. So it could be used with other databases like Oracle Database, Microsoft SQL Server, MySQL, PostgreSQL, SQLite, and some others. But, that would depend on correct configuration and correct query usage for each database. For example, parameters are not always prefixed by # for all databases. But, this particular DBContext passes that problem on to the class that calls it and avoids dealing with it as we can see in UserRepository use of the class.
... and what is its purpose/mission?
The purpose of the DBContext you've shown us, is to get a connection using a factory, instantiate and return a DBCommand with an optional transaction. And, then the Repository base class function ToList seems to be in charge of executing the Command and mapping the objects User.
It looks like this was an attempt to create a repository pattern using ado.net. And, not knowing the history of your application, I can only assume there was a decision made to avoid using Entity Framework. There are many different ways I've seen this done. And, plenty of people will argue about the correct implementation. But, I think that would be out of the scope of this particular question.

Related

How to solve Dapper - UnitOfWork Transaction Error

I'm trying to implement unit of work repository pattern with Dapper in ASP.NET Core Web API.
I have created the model,repository and UOW. When I try to do GET request I got an error
System.InvalidOperationException: BeginExecuteReader requires the
command to have a transaction when the connection assigned to the
command is in a pending local transaction. The Transaction property
of the command has not been initialized.
Here is my controller;
public class CityController : ControllerBase
{
private readonly IUnitOfWork unitOfWork;
public CityController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
[HttpGet]
public async Task<IEnumerable<City>> GetAll()
{
return await unitOfWork.Cities.All();
}
CityRepository.cs
internal class CityRepository : GenericRepository<City>, ICityRepository
{
public CityRepository(IDbTransaction transaction)
: base(transaction)
{
}
public async Task<IEnumerable<City>> All()
{
var model = await Connection.QueryAsync<City>("SELECT * FROM DT_Inspection.City");
return model.ToList();
}
}
public IConfiguration configuration;
private IDbTransaction _transaction;
private IDbConnection _connection;
ICityRepository _cityRepository;
private bool _disposed;
public UnitOfWork(IConfiguration _configuration)
{
configuration = _configuration;
_connection = new SqlConnection(_configuration.GetConnectionString("DefaultConnection"));
_connection.Open();
_transaction = _connection.BeginTransaction();
}
public ICityRepository Cities { get { return _cityRepository ?? (_cityRepository = new CityRepository(_transaction)); }
public void Commit()
{
try
{
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
finally
{
_transaction.Dispose();
_transaction = _connection.BeginTransaction();
resetRepositories();
}
}
For starters, that's the exact opposite of a Unit-of-Work. Unit of work means you have a single, indivisible bunch ( a unit) of operations (work) that needs to be committed or discarded as one. Once it completes, it's gone and can't be reused. That's a feature.
A UoW typically implies that the work doesn't affect the data source until it's committed, but your code starts an expensive long-lived transaction that does lock records from the very first read.
The class you use though creates a global long-lived connection and a global, implicit transaction. That's a very bad practice. These lines specificially, are a major bug :
_transaction = _connection.BeginTransaction();
resetRepositories();
You could achieve the same effect in any database through some connection settings but very few people do this.
Database connections and transactions are meant to be short-lived. Otherwise they accumulate locks and tie up resources on the server, causing blocking or even deadlocks between different transactions. Otherwise you could run into deadlocks or long delays even with a couple of concurrent clients. This was a huge problem in the 1990s before disconnected operations and optimistic concurrency were introduced. What you try to do puts you back in the 1990s.
The difference really is 1000x worse performance, and having to use 10x+ more database servers to handle the same amount of traffic.
That's why the docs, courses and tutorial (the good ones) all show connections and transactions created right before they're used :
using(var cn=new SqlConnection(...))
{
cn.Open();
using(var tx=cn.BeginTransaction())
{
using (var cmd1=new SqlCommand(sql1,cn,tx))
{
...
}
using (var cmd2=new SqlCommand(sql2,cn,tx))
{
...
}
}
}
If you use an explicit database transaction, you must pass the active transaction to the command itself. That's what the exception you got says. The alternative is to use a TransactionScope and create open connections inside it. In this case, the connection is implicitly enrolled in the transaction :
using(var cn=new SqlConnection(...))
{
using(var scope=new TransactionScope())
{
cn.Open();
using (var cmd=new SqlCommand(sql,cn))
{
...
}
...
}
}
Dapper is a thin mapper over ADO.NET, it doesn't replace it. This means you still have to use ADO.NET, connections and transactions correctly. If you want to use to use explicit transactions, you need to pass it through the transaction parameter to Query or Execute:
using(var cn=new SqlConnection(...))
{
cn.Open();
using(var tx=cn.BeginTransaction())
{
var results1=cn.QueryAsync<City>(sql1,transaction:tx);
var results2=cn.QueryAsync<City>(sql2,transaction:tx);
}
}
Or you can use a TransactionScope :
using(var scope=new TransactionScope())
{
using(var cn=new SqlConnection(...))
{
cn.Open();
var results1=cn.QueryAsync<City>(sql1);
var results2=cn.QueryAsync<City>(sql2);
}
}
The implementation leaks already. "Repository" (it's actually a Data Access Object, not a Repository) would need access to the _transaction field's value. Or you could use a TransactionScope and forget about that UoW. After all, access to the database is the DAO/Repository's job, not the UoW's. Maybe you could use the UoW as a thin wrapper over a TransactionScope, or have the Repository create and initialize the UoW with an explicit transaction from the connection it owns.
Assuming you use a TransactionScope, your UoW should be nothing more than a wrapper:
class UnitOfWork:IDisposable
{
TransactionScope _scope=new TransactionScope();
public void Dispose()
{
_scope.Dispose();
}
}
The "repository" shouldn't even know about the UoW. It should control connections though:
internal class CityRepository
{
string _connString;
public CityRepository(IConfiguration configuration)
{
_connString=configuration.GetConnectionString("DefaultConnection")
}
public async Task<IEnumerable<City>> All()
{
using(var cn=new SqlConnection(_connStr))
{
var model = await Connection.QueryAsync<City>("SELECT * FROM DT_Inspection.City");
return model.ToList();
}
}
}
Only the controller would need to create the UoW, and then, only if there's any chance of modifying data. Reads don't need transactions :
public class CityController : ControllerBase
{
private ICityRepository _cityRepo;
public CityController(ICityRepository cityRepo)
{
_cityRepo=cityRepo;
}
[HttpGet]
public Task<IEnumerable<City>> GetAll()
{
return _cityRepo.All();
}
[HttpPost]
public async Task Post(City[] cities)
{
using(var uow=new UnitOfWork())
{
foreach(var city in cities)
{
_cityRepo.Insert(city);
}
}
}

how to organize MySQL database connection when not using Entity Framework (or similiar)

Based on these two samples
https://github.com/jasontaylordev/CleanArchitecture
https://github.com/jasontaylordev/NorthwindTraders
I added an Application and Infrastructure layer to my API project. The important part is that I will only use the MySQL.Data package for the database stuff (no Entity Framework or other helping libraries).
I thought it would be a good practise to define interfaces for repositories in the Application layer
public interface IUsersRepository
{
Task<IList<User>> GetUsers();
Task<User> GetUserByUsername(string username);
// ...
}
and implement them in the Infrastructure layer. So when it comes to the DI container setup via IServiceCollection I can setup those repositories with services.AddTransient(typeof(IUsersRepository), typeof(UsersRepository));. Due to the fact I'm not using an ORM tool I have to setup the connection by myself. That's why I defined an interface in the Application layer
public interface IDatabaseContext
{
DbConnection DatabaseConnection { get; }
}
and create the connection to the MySQL database in the Infrastructure layer
public class DatabaseContext : IDatabaseContext
{
public DbConnection DatabaseConnection { get; }
public DatabaseContext()
{
DatabaseConnection = new MySqlConnection("server=127.0.0.1;uid=root;pwd=12345;database=test");
}
}
To make this injectable I add it to the services collection with services.AddSingleton(typeof(IDatabaseContext), typeof(DatabaseContext));
I think the implementing repositories should only care for their own query because they might get chained for a transaction. Currently they don't take care for the connection
public class UsersRepository : IUsersRepository
{
private readonly IDatabaseContext databaseContext;
public UsersRepository(IDatabaseContext databaseContext)
{
this.databaseContext = databaseContext;
}
public async Task<IList<User>> GetUsers()
{
using (DbCommand getUsersCommand = databaseContext.DatabaseConnection.CreateCommand())
{
// setup command string, parameters and execute command afterwards
}
}
}
The problem is that now every repository call requires a connection handling before execution in the Application layer. By that I mean I have to wrap the call like so
await databaseContext.DatabaseConnection.OpenAsync();
IList<User> users = await usersRepository.GetUsers();
// ...
await databaseContext.DatabaseConnection.CloseAsync();
so the calling class needs to inject the repository and the IDatabaseContext. I'm also not sure if opening/closing the connection for each query / transaction is a good idea.
Maybe there are some better approaches to enhance the current one. I would like to create a self managing database connection. The application layer shouldn't open/close connections. It should only call the repository methods. The repository methods shouldn't do it neither because they might run in a transaction and only the first query should open it and the last one closes it.
It would be awesome to define new repository methods with the SQL logic only and all the connection stuff is handled once. Any ideas?
First, if you enable connection pooling on the MySql connector then you can skip the CloseAsync call and Dispose the connection each time you have used it, that will allow the pooling mechanism of the connector to reuse connections as needed. To enable it add Pooling=True to your connection string.
Second, to avoid all the extra code you can create a base class for the repositories and implement all the connection handling on it, I would create a function that takes a Func<DbConnection,Task<T>> and some type of static factory to reduce code rewrite:
//static DB factory
public static class DBFactory
{
public async Task<DBConnection> GetConnection()
{
//Create here your connection
var newCon = //..
await newCon.OpenAsync();
return newCon;
}
public async Task ExecuteTransaction(Func<DBConnection, MySqlTransaction, Task<bool>> TransactedCode)
{
using(var dbConnection = await GetConnection())
{
var transact = dbConnection.BeginTransaction();
try
{
if(await TransactedCode(dbConnection, transact))
transact.Commit();
else
transact.RollBack();
}
catch{ transact.RollBack(); }
}
}
}
//Base class for repositories
public abstract class BaseRepository
{
protected async Task<T> ExecuteResultWithConnection<T>(Func<DBConnection, MySqlTransaction, Task<T>> RepositoryMethod)
{
using(var dbCon = await DBFactory.GetConnection())
{
return await RepositoryMethod(dbCon, null);
}
}
protected async Task ExecuteWithConnection(Func<DBConnection, MySqlTransaction, Task> RepositoryMethod)
{
using(var dbCon = await DBFactory.GetConnection())
{
await RepositoryMethod(dbCon, null);
}
}
}
//Example of repository
public class TestRepository : BaseRepository
{
public async Task<IList<TestObject>> GetTestObjects(DBConnection con = null, MysqlTransaction Transact = null)
{
if(con != null)
{
//execute the code without calling the base function
//using con as your connection and transact if supplied
return yourResult;
}
else
{
return await ExecuteResultWithConnection(async (dbCon, transact) => {
//Here you have your connection ready to be used as dbCon
//without transaction
return yourResult;
});
}
}
public async Task AddTestObject(TestObject NewObject, DBConnection con = null, MysqlTransaction Transact = null)
{
if(con != null)
{
//execute the code without calling the base function
//using con as your connection and transact if supplied
}
else
{
await ExecuteWithConnection(async (dbCon, transact) => {
//Here you have your connection ready to be used as dbCon
//without transaction
});
}
}
}
Now, calling a repository is totally clean:
var repo = new TestRepository();
var objs = await repo.GetTestObjects();
await repo.AddTestObject(new TestObject{ /* whatever */ });
Also, you can create transactions:
await DBFactory.ExecuteTransaction(async (dbCon, transact) => {
var someObject = repo.GetTestObjects(dbCon, transact);
await repo.AddTestObject(new TestObject{ /* whatever */ }, dbCon, transact);
await repo.AddTestObject(new TestObject{ /* whatever */ }, dbCon, transact);
await repo.AddTestObject(new TestObject{ /* whatever */ }, dbCon, transact);
return true;
//If any of the inserts fails with an exception the transaction
//will be automatically rolled back.
//You can also return false if the transaction must be rolled back.
});
Remember, this is just an example, in the real world you will have a more complex infrastructure, this only gives you an idea of what you could do.

Is there any opportunities to override SQLite Connection Open method in C#?

I need to execute a SQliteCommand every time when Connection opened. And I'm going to to inherit SQliteConnection class and register custom class using Autofac, but this way doesn't work work for me.
Instead of an is a relationship use an has a relationship - Don't inherit SQliteConnection, instead, encapsulate it inside your class.
Something like this should get you started:
public class DBHelper
{
private readonly string _connectionString,
_sqlToExecuteOnConnectionOpen;
public DBHelper(string connectionString)
{
_connectionString = connectionString;
_sqlToExecuteOnConnectionOpen = "Your sql goes here";
}
public void ExecuteSql(Action<SQliteConnection> action)
{
using(var con = new SQliteConnection(_connectionString))
{
using(var cmd = new SQliteCommand(_sqlToExecuteOnConnectionOpen, con)
{
con.Open();
// of course, any parameters goes here...
cmd.ExecuteNonQuery();
}
action(con);
}
}
}
Now you have a method that opens and disposes an instance of SqliteConnection, execute your pre-detenrmined sql, and whatever other action you want to execute with that connection.
You can even take it one step further and have this method private, but expose public methods for ExecuteNonQuery, ExecuteScalar, ExecuteReader and even filling a dataset or data table - and have them all execute this method.
This can save you a lot of the repetitive plumbing code usually written when using ADO.Net.
In fact, I've published a project on GitHub that does exactly that (except your constant sql statement, of course) - You can clone it, view it's code, and just generally take ideas from it and implement them in your own code.
You can use AOP principle to intercept the Open method.
Let's start by implementing your custom interceptor.
public class PrepareConnectionInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (!(invocation.InvocationTarget is IDbConnection
&& invocation.Method.Name == nameof(IDbConnection.Open)))
{
invocation.Proceed();
return;
}
invocation.Proceed();
IDbConnection connection = (IDbConnection)invocation.InvocationTarget;
using(IDbCommand command = connection.CreateCommand())
{
command.CommandText = "SQL statement";
command.ExecuteNonQuery();
}
}
}
Then register your connection and interceptor using autofac
builder.RegisterType<PrepareConnectionInterceptor>()
.AsSelf();
builder.RegisterType<SQLiteConnection>()
.As<IDbConnection>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(PrepareConnectionInterceptor));
Now each time you call the Open method on a resolved IDbConnection the interceptor will be triggered and custom SQL will be executed.
You can find more information on the Autofac Interceptor documentation

Create a new DbContext or use the injected DbContext when calling a stored procedure in Entity Core?

What is the proper way to execute a stored procedure (using the ADO method) in Entity Core?
Should I use the injected dbcontext or should I create a new one?
public class ContactController
{
private readonly MyDbContext _context;
public ContactController(MyDbContext context)
{
_context = context;
}
public IActionResult Search(ContactSearchModel csm, int page)
{
//execute using the injected _context?
using (var command = _context.Database.GetDbConnection().CreateCommand())
{
//...
}
//or create a new context?
using (var newContext = new MyDbContext())
{
using (var command = newContext.Database.GetDbConnection().CreateCommand())
{
//...
}
}
}
}
There shouldn't be any reason to not use the injected one.
If you are asking just because you are only reading the data and want it to be quick, and you're worried about tracking or something, you should just call .AsNoTracking() when calling the DB for your stored proc.
An example:
var users = _context.Users
.FromSql("EXECUTE dbo.MyUserStoredProc")
.AsNoTracking()
.ToList();
See these EF Core docs on Raw SQL and No Tracking.

Entity Framework - Setting session_context using IDbConnectionInterceptor

I'm following this tutorial in order to use Row Level security in SQL Server via Entity Framework 6 CodeFirst. The tutorial code sample shows how to use IDbConnectionInterceptor and set the current user id in session_context. To retrieve the user id, it uses static accessor method HttpContext.Current.User.Identity.GetUserId() which is coupled with Asp.Net identity and System.Web namespace.
In my multi-tenant web app, I wanted to have the tenantId injected into the DbConnectionInterceptor using Unity (without creating hard-coupling with HttpContext) and set the tenantId in the session_context. I found out that the DbConnectionInterceptor needs to be registered globally (eg. at application startup) and therefore you cannot have Unity create DbConnectionInterceptor instance per request.
I also have 2 DbContexts in my solution representing 2 different databases (Tenant database and a system database) and I only want to apply session_context to the Tenant database only.
It seems that the only option remaining to me is have the tenantId injected into the DbContext isntance via Unity and access the DbContext instance inside the Opened() method of the DbConnectionInterceptor. For this purpose I thought of using the interceptionContext parameter in the Opened() method. interceptionContext has a DbContexts(plural) property. There's no documentation on this so I assumed something like this would work:
public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
{
var firstDbContext = interceptionContext.DbContexts.FirstOrDefault(d => d is TenantDataContext);
if (firstDbContext != null)
{
var dataContext = firstDbContext as TenantDataContext;
var tenantId = dataContext.TenantId;
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = $"EXEC sp_set_session_context #key=N'TenantId', #value={tenantId};";
cmd.ExecuteNonQuery();
}
}
My code checks whether the DbContexts collection contains the TenantDataContext as the first element and executes the sp_set_session_context. But what I'm worried about is whether there's any chance for both DbContexts to be there at the same time? If that was the case, the connection to my other database would also set the session_context which I don't need. I'm wondering why Microsoft has provided this as a collection property rather than a single DbContext property. This property makes you wonder whether the same connection can be used by multiple DbContexts.
Is there anyone who has achieved what I want? Any explanation on this interceptionContext would also be helpful for me.
You can use the Connection_StateChaned event of your DbContext if you are using EF like so.
static void Main(string[] args)
{
using (var db = new AdventureWorks2016CTP3Entities())
{
db.Database.Connection.StateChange += Connection_StateChange;
db.Database.Log = (log) => System.Diagnostics.Debug.WriteLine(log);
var purchase = db.SalesOrderHeader.Select(i => i.SalesPersonID);
foreach (var m in purchase)
{
Console.WriteLine(m);
}
}
}
private static void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e)
{
if(e.CurrentState == System.Data.ConnectionState.Open)
{
var cmd = (sender as System.Data.SqlClient.SqlConnection).CreateCommand();
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "exec sp_set_session_context 'UserId', N'290'";
cmd.ExecuteNonQuery();
}
}
I realize this is an older question, but figured I would post our solution for those looking for one.
We are using interceptors to Inject a SQLServer session_context statement into the commands/connections running through EF.
In our case, we had to create Interceptors for DbCommand and DbConnection to handle both EF Linq queries and raw SQL queries that run through Commands. These Interceptor classes implement IDbCommandInterceptor and IDbConnectionInterceptor respectively.
For DbCommandInterceptor, we use the SqlCommand.CommandText to prepend our EXEC sp_set_session_context raw SQL to each command coming through the interceptor.
public class SessionContextDbCommandInterceptor : IDbCommandInterceptor
For DbConnectionInterceptor, we implement the Opened method and execute a SqlCommand against the connection that runs our sp_set_session_context SQL.
public class SessionContextDbConnectionInterceptor : IDbConnectionInterceptor
{
public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
{...}
We then created a DbConfiguration class that adds the interceptors within the constructor:
public class SessionContextConfiguration : DbConfiguration
{
public SessionContextConfiguration()
{
AddInterceptor(new SessionContextDbConnectionInterceptor());
AddInterceptor(new SessionContextDbCommandInterceptor());
}
}
Then add this DbConfiguration class to our DbContext class via the DbConfigurationType Attribute as well as to our web.config:
[DbConfigurationType(typeof(SessionContextConfiguration))]
public class MyContext : DbContext
<entityFramework codeConfigurationType="MyAssembly.SessionContextConfiguration, MyAssembly">
We inject our DbContexts using Autofac as we normally would and the interceptors are automatically added to the DbContext instances because of the Configuration class.

Categories