Linq-to-SQL transaction breaking linked server query - c#

I have a simple application that uses Linq-to-SQL to retrieve and update data.
On my DataContext I have a procedure called GET_DATA that reads data from a non-SQL Server data source using a linked server. I use the data read to update a few tables on SQL Server.
The process of updating the data on SQL Server is done (summarizing) like this:
var db = new MyDBDataContext()
db.ExecuteCommand("Update table1 set field={0} where field={1}", value1, value2);
It works perfectly fine.
Now, the problem: a new requirement came to update 2 tables instead of one so I decided to add some transaction control:
using (var db = new MyDBDataContext())
using (var tran = new TransactionScope())
{
try
{
db.ExecuteCommand("Update table1 set field={0} where field={1}", value1, value2);
db.ExecuteCommand("Update table3 set field={0} where field={1}", value1, value2);
tran.Complete();
}
catch (Exception e)
{
tran.Dispose();
}
}
Once again it works fine, but after I update the data (regardless if the process fails or succeeds), if I try to run the search procedure, I get this error:
The operation could not be performed because OLE DB provider \"OraOLEDB.Oracle\" for linked server \"LINKED_SERVER_NAME\" was unable to begin a distributed transaction."}
The only way I can make it work again is if I right click on the ASP.Net development Server and close it. It seems that it is keeping something open, but I checked the dm_tran_session_transactions DMV and I don't see any transactions open (I can see it if I debug into the method, but once the method is completed, no transaction is open)
FYI: the dbcontext objects are different form the select to the update and I also tried to add db.SubmitChanges() but it didn't work either.
Any advise would be helpful thanks
EDIT:
The search procedure does not have transaction control inside it and it is called by a simple LINQ Statment:
var result = (from r in context.GET_DATA(parameter)
select new Object{
.....
}
);
return result.ToList();

Related

How to run multiptle queries postgresql statements from C#

I am using EntityFramework and code migrations to keep my Postgres database up to date.
Before deployment on a new environment I would like to make sure the database exists and that the user that will execute queries for the application has enough permissions to do this.
So in short I would like to do the following from my Migrations project before running context.Database.Migrate():
Check whether the database exists
If it doesn't exist create it
Create the user if it doesn't exists
Grant permissions to the user
Run migrations
I have two options (I think):
Run multiple statements at once, keeping the logic inside the query itself and avoid having it in code (C#)
Run statements and deciding in code what to do next.
Option 1 has my preference but when I run the statement checking whether the database exists and create it if it doesn't I get an error: CREATE DATABASE cannot be executed from a function.
The query I'm running looks like this:
DO $$
BEGIN
IF NOT EXISTS(SELECT datname FROM pg_database WHERE datname='database_name') THEN
CREATE DATABASE database_name;
END IF;
END
$$
Option 2 would involve running the statement to check if the database exists with the following code:
using (var conn = new NpgsqlConnection("connection"))
{
conn.Open();
var sql = "SELECT 1 FROM pg_database WHERE datname='database_name'";
var command = new NpgsqlCommand(sql, conn);
var dbExists = command.ExecuteScalar();
if(dbExists == null)
{
command = new NpgsqlCommand("CREATE DATABASE database_name", conn);
command.ExecuteNonQuery();
}
conn.Close();
}
The above code works but I think I prefer option 1.
So I have 2 questions:
How can I fix the CREATE DATABASE cannot be executed from a function error.
Is the second option considered 'wrong'?

How to altertable if not exist in c#?

I have an API in .NET Core 3.1 which insert some data to my databases, the issue is that i had to add a new column to the column where i'm inserting the data, as then the api is called i'm dynamically connect to the database to which i should do the insert, how could i make an altertable if that column not exist and then insert the new item?
Here is the method where i'm inserting the stuff:
public static IActionResult InsertOrder(string piva, string idNegozio, RiepilogoTakeaway ordine, HttpResponse Response) {
string connectionString = getConnectionString(piva);
var query_ordine = #"INSERT INTO `ordini` (`TIPO_OR`, `TAVOLO_OR`, `ORA_OR`, `SENT_OR`, `ID_OR_CFG`, `LOTTERIA_OR`) VALUES (#tipo, #tavolo, NOW(), 0, #id, #lotteria); SELECT LAST_INSERT_ID();";
using var connection = new MySqlConnection(connectionString);
using var cmd = new MySqlCommand(query_ordine, connection);
cmd.Parameters.AddWithValue("#tipo", "STB");
cmd.Parameters.AddWithValue("#tavolo", 0);
cmd.Parameters.AddWithValue("#id", idNegozio);
cmd.Parameters.AddWithValue("#lotteria", ordine.lotteria);
connection.Open();
cmd.Prepare();
string idOrdine = cmd.ExecuteScalar().ToString();
...
}
And that's the alter table i should execute:
ALTER TABLE `ordini`
ADD COLUMN `LOTTERIA_OT` VARCHAR(10) NULL AFTER `ID_OR_CFG`;
Which would be the best way to do it?
Try to deploy the SQL changes before calling it. It's very likely that you'll need higher level of privilege in the database to run the alter or add column, for example, if the SQL user you are using to insert the order doesn't have permissions to change the DB schema.
However, if that is still an option, you will need to execute the SQL schema changeusing command.ExecuteNonQuery().
Possibly worth creating a new method "PrepareSqlTable" so you can call before your query runs. Bear in mind that will impact your application performance/scalability because now we'll be checking that on every single query.
Another alternative, handle this in the catch by looking into the error message and creating it accordingly (provides more performance benefits than the previous option).

Concurrency checks with Entity Framework and Stored Procedures

I am using Entity Framework and manipulating data in a sqlserver database via stored procedures (per client request).
Data is pulled from the database via stored procedures and the results of these stored procedures populates a SQLite db in the Winforms Application.
SQLite is used for additional querying and changing of data and then pushed back via update stored procedure to the sql server db when the user syncs
all stored procedures are on sql server (no in text / in line sql in the application)
I am faced with the scenario where multiple users can potentially attempt to update the same field, which poses 2 problems for me.
If they call the same stored procedure at the same time (select or update).
I am not sure what my options are here from a programming level, I don't have rights to make server changes.
if the field they are trying to update has already been updated.
for problem 2 I am trying to build in a check by date stamping the modification. ie. when a user syncs sql server adds that sync date to a date modified column, if a another user tries to modify the same field i want to check the date modified on his sqlite db and compare that to date modified in sql server, if sql server's date modified is more recent, keep sql server values, if syncing user's modified date is more recent use his...
I have looked into Resolving optimistic concurrency with a condition where the client wins.
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
but this seems to only work when you directly query the db with Entity Framework and not when you want to update via stored procedure.
what can I use to perform these types of checks?
Ok, This is probably not the best solution, but it is what I was able to come up with, and although not tested extensively initial once over seems to be ok-ish.
I am not going to mark this as the answer, but its what i got working based on my question above.
calling stored procedure at same time, created a class for the transactions
public class TransactionUtils
{
public static TransactionScope CreateTransactionScope()
{
var transactionOptions = new TransactionOptions();
transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
transactionOptions.Timeout = TransactionManager.DefaultTimeout;
return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
}
}
and then in code use it as follows:
var newTransactionScope = TransactionUtils.CreateTransactionScope();
try
{
using (newTransactionScope)
{
using (var dbContextTransaction = db_context.Database.BeginTransaction(/*System.Data.IsolationLevel.ReadCommitted*/))
{
try
{
db_context.Database.CommandTimeout = 3600;
db_context.Database.SqlQuery<UpdateData>("UpdateProc #Param1, #Param2, #Param3, #Param4, #Param5, #Param6, #DateModified",
new SqlParameter("Param1", test1),
new SqlParameter("Param2", test2),
new SqlParameter("Param3", test3),
new SqlParameter("Param4", test4),
new SqlParameter("Param6", test5),
new SqlParameter("DateModified", DateTime.Now)).ToList();
dbContextTransaction.Commit();
}
catch (TransactionAbortedException ex)
{
dbContextTransaction.Rollback();
throw;
}
As for issue 2 (concurrency)
I could not find a way to use built in concurrency checks between data on SQL Server and the data that I want to update from SQLite (2 different contexts)
So I am storing Date modified in both sql server and sqlite.
the sqlite date modified is updated when the user modifies a record,
date modified on sql server is updated when a sync runs.
Before syncing I query the sqlServer db for the record to be updated's date modified and compare it with the sqlite's date modified for that record in a if statement and then either run the update stored procedure for that record or not

Unable to begin a distributed transaction using Entiy Framework

I'm having the following error executing this piece of code:
private bool _updateList(SysInfo _sysInfo, List<pList> _pList)
{
try
{
foreach (var p in _pList)
{
_context.spUpdatePListApprovalFlow(p.countryID, _sysInfo.User.JobRoleID, p.src, p.id, p.status, _sysInfo.User.Username);
}
return true;
}
catch (Exception ex) //debug only
{
throw; //throws error to the main try catch
}
}
ERROR
The operation could not be performed because OLE DB provider "MSDASQL"
for linked server "AS400_LINKEDSRV" was unable to begin a distributed
transaction.
However, everything works fine when I run the Stored Procedure in SQL Management Studio:
exec [dbo].[spUpdatePListApprovalFlow]
#CountryID = 123456,
#UserTypeID = 23456,
#Src = 1,
#Id = '123456789',
#Status = 30,
#Username = 'username'
I'm tired of digging for an answer nothing works... Few things I've tried:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
insert multiple transactions in the stored procedure
This sp has 2 sub stored procedures on it. One that writes into a table of the application's database, and another that updates a table in as400.
In EF6 stored procedures are called in an explicit transaction, by default. You can turn this off for a particular DbContext instance by changing its configuration after creating it, or for all instances of a DbContext type by changing it in the constructor. EG
using (var db = new Db())
{
db.Configuration.EnsureTransactionsForFunctionsAndCommands = false;
//. . .
}
See: https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbcontextconfiguration.ensuretransactionsforfunctionsandcommands
Ok, after half a day trying to solve this I've finally solved it.
Resolution
Downgraded from Entity Framework 6 to Entity Framework 5 and the
distribuited transactions error has gone.
Just pay attention, if you're going to do this, you have to change some usings in your code. ( in auto generated code in the Data Model as well)
EF 6 uses
using System.Data.Entity.Core.Objects
EF 5 uses
using System.Data.Objects;
If you don't need distributed transactions you can try to disable them in the settings of the linked server:
EXEC master.dbo.sp_serveroption
#server=N'AS400_LINKEDSRV',
#optname=N'remote proc transaction promotion',
#optvalue=N'false'
Refer to this Microsoft page on Linked Servers.
Your System Administrator and/or DBA will probably need to make changes to address the missing linked server definition to your AS/400 server.
Another possible issue is that the AS/400 server (from IBM) lacks software support for the OLE DB data sources. This too would be something that the System Administration staff may need to address.

Script Uncommitted data from table using SMO

I have a C# win form application which has a facility to backup databases with data to a script file while the application running.
I used following code to script the database using SMO;
public StringCollection GenerateDatabaseScript(string databaseName)
{
//Validate database name goes here
StringCollection dbScript = new StringCollection();
//Create db connection
sqlDataAccess.DBConnect(databaseName); //Custom class to do SQL data operations (sqlDataAccess)
//Create server and database objects
var serverConn = new ServerConnection(sqlDataAccess.Connection);
var dbServer = new Server(serverConn);
var database = dbServer.Databases[databaseName];
//Set script database options here
//--
//Set script database tables option here
//--
//Script database creation
//I also use a method 'ScriptObjectWithBatchDelimiter' to add GO delimiter for each command manually.
dbScript.AddRange(ScriptObjectWithBatchDelimiter(database.Script(dbScriptingOptions)).ToArray());
//Set focus to new db
dbScript.Add(string.Format("USE [{0}]", databaseName));
dbScript.Add("GO");
foreach (Table table in database.Tables)
{
//Skip scripting system tables
if (table.IsSystemObject)
continue;
//Script table
dbScript.AddRange(ScriptObjectWithBatchDelimiter(table.EnumScript(tableScriptingOptions)).ToArray());
}
return dbScript;
}
Problem occurs in this line when encountering a table in the database data is not committed ROWLOCK;
table.EnumScript(tableScriptingOptions)
The problem is how can I script data with READUNCOMMITTED? Is there any properties that I can set to achieve this?
The same question is asked here, but the only answer provided not suitable.
UPDATE: Following code is tried (assumed with the Isolation part in the name) but, still not working.
database.SetSnapshotIsolation(true);
I think what you are looking for is this:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
msdn: "Specifies that statements can read rows that have been modified by other transactions but not yet committed."
so wrap your scripting work inside a transaction with Isolation Level "ReadUncommited".
this is a good example how to use transactions in c#:
https://msdn.microsoft.com/en-us/library/5ha4240h(v=vs.110).aspx

Categories

Resources