DbMigrator does not commit changes to database - c#

I'm trying to programmatically initialize a SQL Server database using DbMigrator class from EntityFramework:
var configuration = new MyDataBaseAssembly.Migrations.Configuration();
configuration.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(myConnectionString, "System.Data.SqlClient");
var dbMigrator = new DbMigrator(configuration);
dbMigrator.Update();
using (var context = new MyDataBaseAssembly.MyDataBaseContext(myConnectionString))
{
// do some queries
context.MyTable1.ToList();
}
The weird thing is that the code works fine for all my database assemblies except one. For some reason one of the DB's is not getting initialized, and I'm receiving an exception:
System.Data.SqlClient.SqlException: Invalid object name 'dbo.MyTable1'.
When I open the SQL Server Profiler, I can see that all the migrations are being applied with no errors. However, when I open the database in SQL Server Management Studio, I can see that it contains no tables at all.
After further investigating, I found out that after dbMigrator.Update is executed, the transaction it uses is not committed to the DB (DBCC OPENTRAN outputs that the transaction is still open). And for some reason, the transaction data gets discarded before I try to do some queries using MyDataBaseContext.
What can be the cause of such behavior? Why does dbMigrator.Update commit all the necessary migrations for some databases, but not for others? Can I control dbMigrator transactions manually and force them to commit?

I ran into the same problem. I ended up working around it with
var scriptor = new MigratorScriptingDecorator(dbMigrator);
var script = scriptor.ScriptUpdate(sourceMigration: null, targetMigration: null);
using (var conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(script, conn))
{
cmd.ExecuteNonQuery();
}
}

Related

Can not retrieve a list of tables from an Oracle database - conn.GetSchema("Tables")

I need to retrieve the list the tables in an Oracle database that is defined by a DSN that is using the Oracle ODBC driver.
However, OdbcConnection.GetSchema("Tables") throws an exception ERROR [HYT00] [Oracle][ODBC][Ora]ORA-01013: user requested cancel of current operation\n or ORA-00604: error occurred at recursive SQL level 1 after about 30 seconds.
using (OdbcConnection connection = new OdbcConnection("Driver={Oracle in OraDB18Home1};Dbq=XE;Uid=system;Pwd=mypassword;"))
{
connection.Open();
//Also unsuccessful with "Views" and "Columns", but works with "DataTypes" and "Restrictions"
DataTable schema = connection.GetSchema("Tables");
}
The database is newly installed and is not too big.
I can call GetSchema() without parameters to successfully retrieve all supported schema collections.
I can also successfully run a query against my database:
OdbcCommand command = new OdbcCommand("SELECT * FROM vendors")
{
Connection = connection
};
OdbcDataReader reader = command.ExecuteReader();
You should stop using ODBC. Use ODP.NET - this is gold standard Oracle .NET provider. And use "Managed" version, i.e. Oracle.ManangedDataAccess. This code below will work fine
var conn = new OracleConnection("Data Source=server:1521/sid;password=pwd;user id=usr");
conn.Open();
var tbl = conn.GetSchema();
conn.Close();
Consile.WriteLine(tbl.Rows.Count.ToString());

Use TransactionScope & SqlCommand to keep databases in sync

We track the same information across two databases in tables that have a similar (enough) schema. When we update the data in one database we want to make sure the data stays in sync with the table in the other database.
We use Entity Framework 5 in both databases, so I had originally wanted to simply import a DbContext of the secondary database and use TransactionsScope to make sure the Create/Updates were atomic.
However, I quickly found out that would be a pain to code, since the table names are the same (anyone working in this controller would have to refer to the Product table as <Conext>.Product), so I used a SqlConnection object for the secondary table, but received some results I don't quite undestand.
If I use the syntax below, the two tables will update atomically/everything goes as planned.
var scopeOptions = new TransactionOptions();
scopeOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
scopeOptions.Timeout = TimeSpan.MaxValue;
var sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Monet"].ConnectionString);
sqlConn.Open();
SqlCommand sqlCommand = sqlConn.CreateCommand();
sqlCommand.CommandText = InsertMonetProduct(product);
using (var ts = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
db.Product.Add(product);
db.SaveChanges();
sqlCommand.ExecuteNonQuery();
ts.Complete();
}
However if I use this syntax below the code crashes on the db.SaveChanges() command with the following message:
Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.
var scopeOptions = new TransactionOptions();
scopeOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
scopeOptions.Timeout = TimeSpan.MaxValue;
using (var ts = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
using(var sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Monet"].ConnectionString))
{
sqlConn.Open();
using (SqlCommand sqlCommand = sqlConn.CreateCommand())
{
sqlCommand.CommandText = InsertMonetProduct(product);
sqlCommand.ExecuteNonQuery();
db.Product.Add(product);
db.SaveChanges();
}
ts.Complete();
}
}
Any idea why the first syntax works and the second crashes? From what I've read online this is supposed to be a change made on the database/database server itself.
The second bit of code is causing an error because it is opening multiple database connections within a single TransactionScope. When a program opens a second database connection inside of a single scope, it gets promoted to a distributed transaction. You can read more information about distributed transactions here.
Searching for "multiple database connections in one transaction scope" is going to help you find a lot more StackOverflow posts. Here are two relevant ones:
C# controlling a transaction across multiple databases
How do you get around multiple database connections inside a TransactionScope if MSDTC is disabled?
Before you walk off into the land of distributed transactions though, there may be a simpler solution for this case. Transaction scopes can be nested, and parent scopes will rollback if any of their nested scopes fail. Each scope only has to worry about one connection or just nested scopes, so we may not run into the MSDTC issue.
Give this a try:
var scopeOptions = new TransactionOptions();
scopeOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
scopeOptions.Timeout = TimeSpan.MaxValue;
using (var ts = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
using (var scope1 = new TransactionScope(TransactionScopeOption.Required))
{
// if you can wrap a using statment around the db context here that would be good
db.Product.Add(product);
db.SaveChanges();
scope1.Complete();
}
using (var scope2 = new TransactionScope(TransactionScopeOption.Required))
{
// omitted the other "using" statments for the connection/command part for brevity
var sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Monet"].ConnectionString);
sqlConn.Open();
SqlCommand sqlCommand = sqlConn.CreateCommand();
sqlCommand.CommandText = InsertMonetProduct(product);
sqlCommand.ExecuteNonQuery(); // if this fails, the parent scope will roll everything back
scope2.Complete();
}
ts.Complete();
}

System.InvalidOperationException: Cannot perform CAS Asserts in Security Transparent methods

I have a SQL CLR trigger written in C# 4.0 and deployed on SQL Server 2014. Whenever an insertion happens in a table in SQL Server, this CLR trigger's job is to import that row in an Oracle database. So basically I have to import data in Oracle database whenever an insert query is fired on a table in SQL Server 2014. This is my first CLR SQL trigger project and below is what I am doing:
[SecurityCritical]
[OraclePermission(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]
[SqlTrigger(Name = "FetchSurvey", Target = "temp", Event = "FOR INSERT")]
public static void FetchSurvey()
{
SqlTriggerContext triggerContext = SqlContext.TriggerContext;
// Create result set to store data
DataSet resultSet = new DataSet();
// Create a new SQL command
using (SqlCommand command = new SqlCommand("SELECT * FROM INSERTED"))
{
// Create a new SQL connection
using (command.Connection = new SqlConnection("context connection=true"))
{
// Connect to the database
command.Connection.Open();
// Execute procedure
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(resultSet);
}
// Disconnect from the database
command.Connection.Close();
}
}
SqlPipe sqlP = SqlContext.Pipe;
// Return data
if (resultSet.Tables.Count > 0)
SaveSurvey(resultSet);
sqlP.Send("Finaly its done!!");
}
public static void SaveSurvey(DataSet dsSurvey)
{
using (OracleConnection con = new OracleConnection("my oracle connection string"))
{
if (con.State == ConnectionState.Closed)
con.Open();
DataRowView drv = dsSurvey.Tables[0].DefaultView[0];
using (OracleCommand cmd = new OracleCommand("AddMetaData", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("V_id", drv["TemplateID"]);
cmd.Parameters.AddWithValue("V_Title", drv["TemplateName"]);
cmd.Parameters.AddWithValue("V_CreatedBy", drv["CreatedBy"]);
cmd.Parameters.AddWithValue("V_IsActive", drv["IsActive"]);
cmd.ExecuteNonQuery();
}
}
}
And this is my code to create assembly/deploy trigger:
CREATE ASSEMBLY TriggerImportSurvey
FROM 'C:\ImportSurvey\SQL-CLR-Trigger.dll'
With Permission_Set = External_Access;
Now the problem is whenever I run an insert query in SQL Server to insert data, I got below error in SQL Server:
Msg 6522, Level 16, State 1, Procedure tri_InsertSurvey_clr, Line 18
A .NET Framework error occurred during execution of user-defined routine or aggregate "tri_InsertSurvey_clr":
System.InvalidOperationException: Cannot perform CAS Asserts in Security Transparent methods
System.InvalidOperationException:
at Triggers.FetchSurvey()
tri_InsertSurvey_clr is the trigger which is responsible for executing the assembly whenever I run an insert statement.
Please tell me what I am missing so that I am getting this error, Also if there a more elegant way of implementing a CLR SQL trigger then please also suggest that.
NOTE: When I tried to save the data using a trigger in SQL Server I was successful, but now when I am trying to save it in Oracle database, I am getting this error. Also the Oracle database is installed on another machine.

.net application's oracle connection timing out

i have the code below trying to do a bulk copy from oracle to SQL server 2005 and it keeps timing out. how can i extend the oracle connection timeout? it seems i can not from what i read on the web.
OracleConnection source = new OracleConnection(GetOracleConnectionString());
source.Open();
SqlConnection dest = new SqlConnection(GetSQLConnectionString() );
dest.Open();
OracleCommand sourceCommand = new OracleCommand(#"select * from table");
using (OracleDataReader dr = sourceCommand.ExecuteReader())
{
using (SqlBulkCopy s = new SqlBulkCopy(dest))
{
s.DestinationTableName = "Defects";
s.NotifyAfter = 100;
s.SqlRowsCopied += new SqlRowsCopiedEventHandler(s_SqlRowsCopied);
s.WriteToServer(dr);
s.Close();
}
}
source.Close();
dest.Close();
here is my oracle connection string:
return "User Id=USER;Password=pass;Data Source=(DESCRIPTION=" +
"(ADDRESS=(PROTOCOL=TCP)(HOST=14.12.7.2)(PORT=1139))" +
"(CONNECT_DATA=(SID=QCTRP1)));";
You can set s.BulkCopyTimeout option
In your connection string, there is a 'Connection Lifetime' and 'Connection Timeout' parameter. You can set it accordingly. See here for the full reference.
BTW, I know you didn't ask this, but have you considered an ETL tool for migrating your DB records (e.g. Informatica, FME, etc.)? While your approach is valid, it isn't going to be very performant since you are hydrating all of the records from one DB to the client and then serializing them to another DB. For small bulk sets, this isn't a big issue, but if you were processing hundreds of thousands of rows, you might want to consider an official ETL tool.

TransactionScope works in some places and not in others

Using ASP.NET 3.5, Linq to SQL, SQL Server 2005 on Windows Server 2003. Running VS 2008 on XP SP3 locally.
We need to be able to wrap inserts, updates, and deletes in a transaction. When we first tried this by wrapping code blocks with using(var trans = new TransactionScope()) { ...; trans.Complete(); }, we got an appropriate exception telling us we needed to enable network access for remote transactions. We did so and things began to work the way we expected.
Fast-forward to today. There is a little-used part of our app that also received a TransactionScope treatment. Though transactions work properly in all other parts of our codebase, we discovered today that this seldom used piece is throwing the same “Network Access” exception as before:
Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool. http://img101.imageshack.us/img101/5480/msdtcnetworkaccesserror.jpg
Here's the code that causes the exception:
using (TransactionScope trans = new TransactionScope(TransactionScopeOption.Required, TimeSpan.MaxValue))
{
using (var dc = new ChargeXferDataContext())
{
//create 'Line' object and set initial values
Line line = new Line();
line.Unit_Num = UnitId;
line.SubmittedBy = Viewer.Alias();
line.LineSubmittedOn = DateTime.Now;
//get codes to move from checked rows
//iterate rows in current gridview
foreach (GridViewRow row in gv.Rows)
{
//if checked, insert move order
HtmlInputCheckBox cb = (HtmlInputCheckBox)row.FindControl("RowLevelCheckBox");
if (cb.Checked)
{
//1st: get required values
int id = Convert.ToInt32(((TextBox)row.FindControl("fldCodeId")).Text);
int newId = Convert.ToInt32(((DropDownList)row.FindControl("ddlNewId")).SelectedValue);
char newPOA = Convert.ToChar(((DropDownList)row.FindControl("ddlPOA")).SelectedValue);
//2nd: get current diag code from old patient
//######## Exception happens here...
DiagCode code = dc.DiagCodes.SingleOrDefault(c => c.Id == id);
//########
//3rd: add code to emenline object
addCode(line, code, newId, newPOA);
}
}
dc.SubmitChanges();
trans.Complete();
}
}
If you've got any suggestions, they would be appreciated. Let me know if I can explain something more. Thanks in advance!!

Categories