How to get a SqlTransaction object from a EF Core 5 DBContext transaction object as I need to pass it to SqlBulkCopy operation - ef-core-5.0

I'm trying to start a transaction on DbContext and doing some operation on context and then wants to pass the same transaction to SqlBulkCopy operation as 3rd parameter.
In EF Core 5, DbContent class I did not find a way to get a SqlTransaction/DbTransaction object which can be passed to SqlBulkCopy operation. In code _contextTransaction object is of type DbContextTransaction and It seems it is a breaking change from MS in latest EF Core. I have added [not compatible] in the code for easy understanding.
If someone can suggest a workaround/solution.
using (var _context = new DBContext())
{
using (var _contextTransaction = _context.Database.BeginTransaction())
{
_context.SomeEntity.Add(obj);
_context.SaveChanges();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(_context.Database.GetDbConnection() as SqlConnection, SqlBulkCopyOptions.Default, _context.Database.CurrentTransaction [not compatible]))
{
bulkCopy.DestinationTableName = "SomeTable";
bulkCopy.BulkCopyTimeout = 10000;
bulkCopy.BatchSize = 10000;
...........
await bulkCopy.WriteToServerAsync(partition);
}
}
}

Related

TransactionScope: nested transactions with different database connections (SQL Server & Postgresql)

I am writing an SDK method with transaction using NpgsqlConnection for others to use.
When they were calling my method, they used SqlConnection with another transaction to wrap their DB stuff and my SDK's DB stuff.
If I set my SDK method without a transaction, the outer code was fine and my SDK method could be rolled back. (Which was odd too. Still figuring out why.)
If I set my SDK method with a transaction though, the outer code crashed with a TransactionAbortedException:
System.Transactions.TransactionAbortedException : The transaction has aborted.
---- Npgsql.PostgresException : 55000: prepared transactions are disabled
Currently we're using enlist=false at the SDK's connection string to prevent the inner transaction from joining the outer one but I'd like to know the reason behind this behavior.
Here's the code I'm reproducing the problem with:
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted,
},
TransactionScopeAsyncFlowOption.Enabled))
{
await using (var conn = new SqlConnection(#"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"))
using (var cmd = new SqlCommand("insert into [Test].[dbo].[Test] (Id, \"Name\") values (1, 'A')", conn))
{
await conn.OpenAsync();
var result = await cmd.ExecuteNonQueryAsync();
await SdkMethodToDoStuffWithNpgsql(1);
scope.Complete();
}
}
I had SdkMethodToDoStuffWithNpgsql() to mock a method in a repository with Postgres context injected.
public async Task SdkMethodToDoStuffWithNpgsql(long id)
{
var sqlScript = #"UPDATE test SET is_removal = TRUE WHERE is_removal = FALSE AND id = #id;
INSERT INTO log(id, data) SELECT id, data FROM log WHERE id = #id";
using (var scope = new TransactionScope(
TransactionScopeOption.RequiresNew,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted,
},
TransactionScopeAsyncFlowOption.Enabled))
{
await using (var conn = new NpgsqlConnection(this._context.ConnectionString))
{
await conn.OpenAsync();
using (var cmd = new NpgsqlCommand(sqlScript, conn))
{
cmd.Parameters.Add(new NpgsqlParameter("id", NpgsqlDbType.Bigint) { Value = id });
await cmd.PrepareAsync();
var result = await cmd.ExecuteNonQueryAsync();
if (result != 2)
{
throw new InvalidOperationException("failed");
}
scope.Complete();
}
}
}
}
The above is the expected behavior - enlisting two connections in the same TransactionScope triggers a "distributed transaction"; this is known in PostgreSQL terminology as a "prepared transaction", and you must enable it in the configuration (this is the cause of the error you're seeing above). If the intention is to have two separate transactions (one for SQL Server, one for PostgreSQL) which commit separately, then opting out of enlisting is the right thing to do. You should also be able to use TransactopScopeOption.Suppress.
Note that distributed transactions aren't currently supported in .NET Core, only in .NET Framework (see this issue). So unless you're on .NET Framework, this won't work even if you enable prepared transactions in PostgreSQL.

Cannot execute Raw Sql in .Net COre

I am using Core 2.0 using entity framework.
I have successfully generated context using scaffold DBContext.
I have DBSet for table EMployee.
I need to execute SToredProcedure which will give list of employee.
I cannot see .FromSql nor.ExecuteCommand option.
I have added EntityFrameworkCore.SqlServer(2.0.1),EntityFrameworkCore.SqlServer.Design(1.1.5),Microsoft.VisualStudio.Web.CodeGeneration.Design(2.0.2) and EntityFrameworkCore.Tools(2.0.1) but to no awail.
Please guide for mentioned concerns.
If you want to execute row SQL using EF Core, try the following.
var employees = context.Employees
.FromSql("SELECT * FROM dbo.Employees")
// If you want to execute a stored procedure, then below
// .FromSql("EXECUTE {SP_NAME}")
.ToList();
But note, there are certain limitations present as described here:
https://learn.microsoft.com/en-us/ef/core/querying/raw-sql#limitations
This is the only way to execute Raw SQL in .NET at the moment:
var conn = _context.Database.GetDbConnection();
try
{
await conn.OpenAsync();
using (var command = conn.CreateCommand())
{
command.CommandText = "SELECT * From Table1 WHERE sender = #sender";
DbParameter sender = command.CreateParameter();
sender.ParameterName = "sender";
sender.Value = "Value";
command.Parameters.Add(sender);
DbDataReader reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
int SubscriptionID = reader.GetInt32(0);
}
}
reader.Dispose();
}
}
finally { conn.Close(); }
You can use it for stored procedures as well.

Backup of the database in entity framework

i working with entity famework i need to transfer that code
RESTORE DATABASE [showing8-5-2013] FROM DISK = N'C:\Program Files (x86)\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\Backup\Company.bak' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 10
to code Entity frame work
any help thanks
EF is a DB neutral provider concept. Such commands are by their nature DB specific. EF exposes a way to execute an SQL command:
MyContext.Database.ExecuteSqlCommand();
But you may as well just do it directly.
Pass your SQL command into a custom routine eg:
private static bool ExecuteSqlStatement(string connectionString, string statement) {
int rowsAffected;
using (var sqlConnection = new SqlConnection(connectionString)) {
using (var sqlCommand = new SqlCommand(statement, sqlConnection)) {
try {
sqlConnection.Open();
rowsAffected = sqlCommand.ExecuteNonQuery();
}
catch (Exception ex) {
// your handler or re-throw....
return false;
}
}
}
return rowsAffected == -1;
// see http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx
}

Cannot Perform Linq-To-Sql Stored Procedures with MVC-Mini-Profiler

I am using the MVC-Mini-Profiler in my ASP.net 4.0 C# environment along with MSSQL and Linq-to-SQL. I am having an issue with using L2S with the profiler. Whenever I return new DataClassesDataContext(), it allows me to get data from L2S stored procedures. But if try to return the Mvc-Mini-Profiler ProfilesDbConnection, I can get the data from the stored procedure on the first time after I build, but then ever time after that, zero data is returned. When it returns the DataClassesDataContext using ProfiledDbConnection, I can still iterate through the Db tables, but for some reason, the stored procedures do not allow me to send/receive data. Any ideas on why this might be?
try
{
var miniProfiler = MiniProfiler.Current;
var connstring = new DataClassesDataContext().Connection.ConnectionString;
SqlConnection connection = new SqlConnection(connstring);
var profiledConnection = ProfiledDbConnection.Get(connection);
var context = new DataClassesDataContext(profiledConnection);
return context;
}
catch
{
return new DataClassesDataContext();
}

cleanest way to access sproc data in asp.net

I haven't accessed data using SqlCommand etc. for a while as I tend to use NHibernate these days. I am just wondering whether the following code could be improved. I have tried to use best practises (after some google-ing) and potential exceptions are caught at a higher layer.
[WebMethod]
public XmlDocument GetClassRegistrationReport()
{
XmlDocument doc = new XmlDocument();
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["bla"].ToString()))
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "bla";
command.CommandType = CommandType.StoredProcedure;
connection.Open();
doc.Load(command.ExecuteXmlReader());
}
}
return doc;
}
Thanks!
Best wishes,
Christian
There are a few ways you could improve it a little:
Although the WebMethod pulls data and returns it verbatim with no input parameters, I would suggest seperating service interface and the data into seperate classes. It may make things easier to maintain at a later date.
Assuming there are other DB calls in your framework you may want to consider a helper method in your data layer that wraps up the invocation of a stored procedure. This way you only have one method that all SP calls filter down into which again will make things easier to maintain in the future.
Make the 'bla' key for your connection string setting a constant, this way you can easily reuse and change.
The same applies to the name of the stored procedure, alternatively make it part of your web.config - this means you can change the stored proc name without having to recompile.
If an exception is throw there is no handling for this so the exception will bubble out to the caller, consider catching and handling/logging exceptions. That said you do mention that you are handling exceptions at a higher layer, so I assume this is being done in whatever is calling your webservices.
You should be disposing the SQL command object (in the finally of the try/catch/finally if you do implement exception handling)
EDIT : Code Sample
public class MyWebService
{
[WebMethod]
public XmlDocument GetClassRegistrationReport()
{
return DataLayer.GetClassRegistrationReport();
}
}
// Notice that this is a static internal class, internal to hide the
// data access class from everything but this library and static because
// we don't need instances and using statics will optimise a little.
internal static class DataLayer
{
private const string SP_GetRegistrationReport = "GetRegistrationReport";
private const string Config_DBConnectionString = "PrimaryDB";
private static string GetDB
{
get
{
string dbConnectionString = ConfigurationManager.ConnectionStrings[Config_DBConnectionString].ConnectionString;
if (string.IsNullOrEmpty(dbConnectionString))
{
// This error should could/should be in a resource file.
throw new ConfigurationException("Database connection string is not defined");
}
return dbConnectionString;
}
}
internal static XmlDocument GetClassRegistrationReport()
{
XmlDocument doc = new XmlDocument();
using (SqlConnection connection = new SqlConnection())
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = SP_GetRegistrationReport;
command.CommandType = CommandType.StoredProcedure;
connection.Open();
doc.Load(command.ExecuteXmlReader());
}
}
return doc;
}
}

Resources