Does Npgsql provider has support for TransactionScope? - npgsql

I'm trying to use a TransactionScope with the Npgsql provider.
I found in an old question (provider for PostgreSQL in .net with support for TransactionScope) that Npgsql didn't supported it yet.
Now, after about 5 years, does Npgsql support TransactionScope?
I made a test for myself, using Npgsql 3.0.3 and using the following code:
using (var scope = new TransactionScope())
{
using(var connection = new Npgsql.NpgsqlConnection("server=localhost;user id=*****;password=*****database=test;CommandTimeout=0"))
{
connection.Open();
var command = new NpgsqlCommand(#"insert into test.table1 values ('{10,20,30}', 2);");
command.Connection = connection;
var result = command.ExecuteNonQuery();
// scope.Complete(); <-- Not committed
}
}
Anyone can confirm that Npgsql doesn't support TransactionScope?
EDIT #1
After the confirmation of the support of Npgsql to the TransactionScope, I found that you need to be sure to haev Distribuited Transaction enabled in your PostgreSQL configuration, checking the max_prepared_transactions parameter in the postgres.conf file (remember to restart your server).
EDIT #2
I enabled the Distribuited Transaction on my server but now I got an error using the TransactionScope with Npgsql.
This is my code:
using (var sourceDbConnection = new NpgsqlConnection(SourceConnectionString))
using (var destinationDbConnection = new NpgsqlConnection(DestinationConnectionString))
using (var scope = new TransactionScope())
{
sourceDbConnection.Open();
destinationDbConnection.Open();
Logger.Info("Moving data for the {0} table.", TableName.ToUpper());
var innerStopWatch = new Stopwatch();
innerStopWatch.Start();
foreach (var entity in entities)
{
var etlEntity = new EtlInfoItem
{
MigratedEntityId = category.RowId,
ProjectId = category.ProjectId,
ExecutionDatetime = DateTime.Now
};
// Insert into the destination database
var isRowMigrated = InsertEntity(entity, DestinationSchema, destinationDbConnection);
if (isRowMigrated)
{
// Update the ETL migration table
InsertCompletedEtlMigrationEntity(etlEntity, EtlSchema, sourceDbConnection);
}
else
{
// Update the ETL migration table
InsertFailedEtlMigrationEntity(etlEntity, EtlSchema, sourceDbConnection);
}
}
Logger.Info("Data moved in {0} sec.", innerStopWatch.Elapsed);
Logger.Info("Committing transaction to the source database");
innerStopWatch.Restart();
scope.Complete();
innerStopWatch.Stop();
Logger.Info("Transaction committed in {0} sec.", innerStopWatch.Elapsed);
}
When the TransactionScope exits from the scope (when exiting the using statement), I get a Null Reference Exception with the following stack trace:
Server stack trace:
at Npgsql.NpgsqlConnector.Cleanup()
at Npgsql.NpgsqlConnector.Break()
at Npgsql.NpgsqlConnector.ReadSingleMessage(DataRowLoadingMode dataRowLoadingMode, Boolean returnNullForAsyncMessage)
at Npgsql.NpgsqlConnector.ReadExpectingT
.........
It happens randomly.

Npgsql does support TransactionScope and has done so for quite a while. However, at least for the moment, in order to have your connection participate in the TransactionScope you must either:
Include Enlist=true in your connection string, or
Call NpgsqlConnection.EnlistTransaction
Take a look at the Npgsql unit tests around this for some examples.

Related

MSSQL replication triggers, how to handle conditional HasTrigger in EntityFrameworkCore

I am using EntityFrameworkCore version 7 to implement data access across a number of client databases.
I have recently run into the error 'Could not save changes because the target table has database triggers.' on one of the clients. The error is obviously self explanatory and I understand how to fix it using HasTrigger.
The problem is that this error has occurred because this specific client is replicated and has what I assume are auto generated triggers MSmerge_upd, MSmerge_ins, MSmerge_del. Concurrently the majority of my clients are not replicated and would therefore not have any of these triggers in their database.
So, what is the correct way to handle replication triggers in EntityFrameworkCore particularly when your clients have a mishmash where some are replicated and some are not? Is there a way to check inside IEntityTypeConfiguration if you are running on a replicated database and conditionally add the replication triggers? Is there some sort of best practice in terms of how to handle this scenario with the new HasTriggers requirement?
Given that nobody has posted any answer I will post what my workaround is for now.
I have created a class called AutoTriggerBuilderEntityTypeConfiguration which basically attempts to configure all the triggers for a given EF model.
There are some performance implications with this approach and it could potentially be improved by caching the triggers for all tables across the database but its sufficient for my use case.
It looks like this:
public abstract class AutoTriggerBuilderEntityTypeConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
where TEntity : class
{
private readonly string _connectionString;
public AutoTriggerBuilderEntityTypeConfiguration(string connectionString)
{
this._connectionString = connectionString;
}
public void Configure(EntityTypeBuilder<TEntity> builder)
{
this.ConfigureEntity(builder);
var tableName = builder.Metadata.GetTableName();
var tableTriggers = this.GetTriggersForTable(tableName);
var declaredTriggers = builder.Metadata.GetDeclaredTriggers();
builder.ToTable(t =>
{
foreach (var trigger in tableTriggers)
{
if (!declaredTriggers.Any(o => o.ModelName.Equals(trigger, StringComparison.InvariantCultureIgnoreCase)))
t.HasTrigger(trigger);
}
});
}
private IEnumerable<string> GetTriggersForTable(string tableName)
{
var result = new List<string>();
using (var connection = new SqlConnection(this._connectionString))
using (var command = new SqlCommand(#"SELECT sysobjects.name AS Name FROM sysobjects WHERE sysobjects.type = 'TR' AND OBJECT_NAME(parent_obj) = #TableName", connection)
{
CommandType = CommandType.Text
})
{
connection.Open();
command.Parameters.AddWithValue("#TableName", tableName);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
result.Add(reader.GetString("Name"));
}
}
return result;
}
public abstract void ConfigureEntity(EntityTypeBuilder<TEntity> builder);
}

Executing a non-query requires a transaction

I migrated my code from WebApi2 to NET5 and now I have a problem when executing a non-query. In the old code I had:
public void CallSp()
{
var connection = dataContext.GetDatabase().Connection;
var initialState = connection.State;
try
{
if (initialState == ConnectionState.Closed)
connection.Open();
connection.Execute("mysp", commandType: CommandType.StoredProcedure);
}
catch
{
throw;
}
finally
{
if (initialState == ConnectionState.Closed)
connection.Close();
}
}
This was working fine. After I migrated the code, I'm getting the following exception:
BeginExecuteNonQuery 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.
So, just before calling Execute I added:
var ct = dataContext.GetDatabase().CurrentTransaction;
var tr = ct.UnderlyingTransaction;
And passed the transaction to Execute. Alas, CurrentTransaction is null, so the above change can't be used.
So then I tried to create a new transaction by doing:
using var tr = dataContext.GetDatabase.BeginTransaction();
And this second change throws a different exception complaining that SqlConnection cannot use parallel transactions.
So, now I'm in a situation where I originally had no problem to having neither an existing transaction nor can I create a new one.
How can I make Dapper happy again?
How can I make Dapper happy again?
Dapper has no opinion here whatsoever; what is unhappy is your data provider. It sounds like somewhere, somehow, your dataContext has an ADO.NET transaction active on the connection. I can't tell you where, how, or why. But: while a transaction is active on a connection, ADO.NET providers tend to be pretty fussy about having that same transaction explicitly specified on all commands that are executed on the connection. This could be because you are somehow sharing the same connection between multiple threads, or it could simply be that something with the dataContext has an incomplete transaction somewhere.

Return multiple tables in one SQL Connection using Entity Framework

When i use Entity Framework Profiler the following code makes 3 calls to the database.
using (var entities = new Entities())
{
var faqs = entities.Table1.ToList();
var latest = entities.Table2.ToList();
var inst = entities.Table3.ToList();
}
I would like to make one database call, is there anyway to do this without calling a stored procedure?
I am trying to eliminate database calls throughout my application.
You can do a cross join in ef,
See this
http://geekswithblogs.net/berthin/archive/2012/05/25/how-to-perform-cross-join.aspx
My problem was solved by using the nuget package EntityFramework.Extended.
So using my original code, this is how you would solve my issue.
using (var entities = new Entities())
{
var faqs = entities.Table1.Future();
var latest = entities.Table2.Future();
var inst = entities.Table3.Future();
inst.ToList();
}
When you call ToList() a batch call is sent to your database in one connection.
The reference link is below:
https://github.com/loresoft/EntityFramework.Extended/wiki/Future-Queries

When Entity Framework opens/closes a connection repeatedly, is it the same actual connection?

I'm using Entity Framework 4.x. When using a single instance of a Context for repeatedly querying the database in a single business operation, I know that Entity Framework will open and close the database connection for each query. But my question is, will the connection for all three queries (again, the same instance of the Context) be the same connection to the Database? Or will it grab another connection (potentially a poisoned one) from the Connection Pool?
Example 1:
var conn = (SqlConnection) ((EntityConnection) Context.Connection).StoreConnection;
conn.Open();
var A = new SqlCommand("exec SProcA", conn).ExecuteNonQuery();
conn.Close();
Thread.Sleep(100);
conn.Open();
var B = new SqlCommand("exec SProcB", conn).ExecuteNonQuery();
conn.Close();
Thread.Sleep(100);
conn.Open();
var C = new SqlCommand("exec SProcC", conn).ExecuteNonQuery();
conn.Close();
Example 2:
// Let Entity Framework manage my connections for me automagically.
var A = Context.TableA.First();
var B = Context.TableB.First();
var C = Context.TableC.First();
In these scenarios, will A, B, and C use the same real connection to the database or does Connection Pooling abstract this away from me?
After some testing, it seems that it is indeterminable whether or not you keep working with the same connection. Many times you do but seems like sometimes you don't. Could be concurrency-related. Ultimately, it's probably safest to assume that it's ambiguous and will change.

Setup Database before run ui coded tests on Visual Studio 2010

I'm automating UI tests to my Silverlight App and I'm using Visual Studio 2010 for it. Some tests required a setup to my Oracle Database.
Things i've done:
1 - A setup.sql file where I connect to my Database and perform actions on it. I had this file as an existing item to my project and I add as a Deployment on TestSettings.
Code:
CONNECT USERNAME#DATABASE,
CREATE TABLE TABLE_NAME,
EXIT
2 - A set.bat file where I call the setup.sql file. I had the path of this file on Setup and Cleanup tab on TestSetings.
Code:
sqlcmd -S MARIALISBOA -i setup.sql
3 - I wrote a TestInitilize method on my TestClass.
Code:
[TestInitialize()]
public void Initialize()
{
System.Diagnostics.Process.Start("setup.bat");
}
4 - I connected do my Database throw Visual Studio (Data -> Add New Data Source).
I run a test on Visual Studio but the class isn't created on my database.
Could anyone help me? I'm trying to solve this problem since Monday and I starting to lose my mind
While it does not solve your initial problem, a solution would be to use something similiar to this;
Do not create the table within your tests. this should be created on install of the Test Environment
Clear down the table for each test you want to do using a Helper Method within the test.
For example (Please note that this is SQL Server, use OLE DB connection or similiar);
internal static object FireSqlStatement(string sqlStatement)
{
object result = null;
using (var cn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionString].ConnectionString))
{
cn.Open();
var cmd = new SqlCommand
{
CommandText = sqlStatement,
CommandType = CommandType.Text,
Connection = cn
};
result = cmd.ExecuteScalar();
cmd.Dispose();
cn.Close();
}
return result;
}
An example of how I use this within my test;
[Test]
public void GetUserTriggers()
{
//Insert Test Record
Helper.FireSqlStatement("INSERT INTO [Users].[Triggers] (Id) VALUES (1)");
var request = new GetTriggersRequest()
{
TriggerType = TriggerType.UserTrigger
};
var response = ServiceInvoker.Invoke<IReports,
GetTriggersRequest, GetTriggersResponse>(
"Reports",
request,
(proxy, req) => proxy.GetTriggers(req));
foreach (var t in response.Triggers)
{
Console.WriteLine(t.Id.ToString());
}
Assert.NotNull(response);
Assert.NotNull(response.Triggers);
Assert.Greater(response.Triggers.Count, 0);
}
In your case, you could call;
Helper.FireSqlStatement("TRUNCATE TABLE tableName");
Any good?

Resources