Mongo DB with C# - document added regardless of transaction - c#

I'm trying to test the newly supported transactions in Mongo DB with a simple example I wrote.
I'm using Mongo DB version 4.0.5 with driver version 2.8.1.
It's only a primary instance with no shards/replicas.
I must be missing something basic in the following code.
I create a Mongo client, session & database, then start a transaction, add a document and abort the transaction. After this code, I expect nothing to change in the database, but the document is added. When debugging I can also see the document right after the InsertOne() by using Robo 3T (Mongo client GUI).
Any idea what am I missing?
var client = new MongoClient("mongodb://localhost:27017");
var session = client.StartSession();
var database = session.Client.GetDatabase("myDatabase", new MongoDatabaseSettings
{
GuidRepresentation = GuidRepresentation.Standard,
ReadPreference = ReadPreference.Primary,
WriteConcern = new WriteConcern(1,
new MongoDB.Driver.Optional<TimeSpan?>(TimeSpan.FromSeconds(30))),
});
var entities = database.GetCollection<MyEntity>("test");
session.StartTransaction();
// After this line I can already see the document in the db collection using Mongo client GUI (Robo 3T), although I expect not to see it until committing
entities.InsertOne(new MyEntity { Name = "Entity" });
// This does not have any effect
session.AbortTransaction();
Edit:
It's possible to run MongoDB as a 1-node replica set, although I'm not sure what's the difference between a standalone and a 1-node replica set.
See my post below.
In any case, to use the started transaction the insertion code must receive the session as a parameter:
entities.InsertOne(session, new MyEntity { Name = "Entity" });
With these 2 change now the transaction works.

This is inherently a property of MongoDB itself. (More here and here)
Transactions are only available in a replica set setup
Why isnt it available for standalone instances?
With subdocuments and arrays, document databases (MongoDB) allow related data to be unified hierarchically inside a single data structure. The document can be updated with an atomic operation, giving it the same data integrity guarantees as a multi-table transaction in a relational database.

I found a solution, although not sure what the consequences are, maybe someone can point it out:
It seems it's possible to use Mongo DB as a 1-node replica set (instead of a standalone) by simply adding the following in the mongod.cfg file:
replication:
replSetName: rs1
Also, thanks to the following link the code should use the correct overload of InsertOne() which receives the session as the first parameter (see the edit on the original post):
multiple document transaction not working in c# using mongodb 4.08 community server

Related

SQLite C# / Insert Command adds no data into the database

I've got a problem resolving my SQLite Database issue.
As the title says, I'm "simply" trying to add some data into a particular table of the database file using SQLite - with the Microsoft.Data.SQLite package.
The code is executed without any errors and even the SQL-Statement execution returns that one row altered. But when I take a look into the db with the db-browser the "inserted" row, simply isn't there at all. This is the code executed:
SqliteConnectionStringBuilder _sqlconsb = new SqliteConnectionStringBuilder();
_sqlconsb.DataSource = _SqliteDatabase;
_sqlconsb.Mode = SqliteOpenMode.ReadWrite;
SqliteConnection _sqlconn = new SqliteConnection(_sqlconsb.ConnectionString);
_sqlconn.Open();
SqliteCommand _sqlcmd = _sqlconn.CreateCommand();
_sqlcmd.CommandText = #"INSERT INTO Mediatypes (Name, Type) VALUES ('Hurrey-1', 'i')";
int _numInsert = _sqlcmd.ExecuteNonQuery();
_sqlconn.Close();
return _numInsert;
I created a complete new WPF-Application with some buttons and executed the same piece of code in that new app. There it also executes without any issues and the data is indeed added to the db file, which I again checked with the db browser.
I've got no idea, with this particular code adds the new row of data in the test app, but does'nt do it's job in my app. I'm also complete lost in further debbuging this issue, because it's executed without any issues.
One more point to consider, I've other methods in my app which are able to add data successfully to the database into other tables. So i think it sholdn't be the database file itself nor the MS.Data.SQLite package in my solution.
I hope there's anyone out there who's able to point me into the right direction to get this debbuged and or solved ...!

DbMigrator does not detect pending migrations after switching database

EntityFramework migrations become useless after switching to new Context.
DbMigrator is using list of Pending Migrations from first database instance, which makes means no migrations are applied to other databases, which then leads to errors during Seed();
C# .NET 4.5 MVC project with EF 6
MS SQL Server 2014, multiple instances of same database model.
CodeFirst approach with migrations.
DbContext initializer is set to null.
On Application Start we have custom Db Initialization to create and update databases. CreateDatabaseIfNotExists is working as intended, new databases have all migrations applied. However both MigrateDatabaseToLatestVersion initializer and our custom one are failing to update databases other than first one on list.
foreach (var connectionString in connectionStrings)
{
using (var context = new ApplicationDbContext(connectionString))
{
//Create database
var created = context.Database.CreateIfNotExists();
var conf = new Workshop.Migrations.Configuration();
var migrator = new DbMigrator(conf);
migrator.Update();
//initial values
conf.RunSeed(context);
}
}
context.Database.CreateIfNotExists(); works correctly.
migrator.GetLocalMigrations() is always returning correct values.
migrator.GetPendingMigrations() after first database is returning
empty list.
migrator.GetDatabaseMigrations() is mirror of pending migrations,
after first database it contains full list event for empty databases.
Fetching data (context.xxx.ToList()) from Db instance confirms connection is up and working, and links to correct instance.
Forcing update to most recent migration with migrator.Update("migration_name"); changes nothing. From what I gather by reading EF source code, it checks pending migration list on its own, which gives it faulty results.
There seems to be some caching going in under the hood, but it eludes me how to reset it.
Is there a way to perform migrations on multiple databases or is it yet another "bug by design" in EF?
Edit:
Real problem is DbMigrator creating new Context for its own use. It does it via default parameterless constructor, which in my case had fallback to default (first) connection string in web.Config.
I do not see good solution for this problem but primitive workaround in my case is to temporarily edit default connection string:
var originalConStr = WebConfigurationManager.ConnectionStrings["ApplicationDbContext"].ConnectionString;
var setting = WebConfigurationManager.ConnectionStrings["ApplicationDbContext"];
var fi = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
//disable readonly flag on field
fi.SetValue(setting, false);
setting.ConnectionString = temporaryConnectionString; //now it works
//DO STUFF
setting.ConnectionString = originalConStr; //revert changes
Cheat from: How do I set a connection string config programatically in .net?
I still hope someone will find real solution so for now I will refrain with self-answer.
You need to correctly set DbMigrationsConfiguration.TargetDatabase property, otherwise the migrator will use the default connection info.
So in theory you can do something like this
conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(...);
Unfortunately the only 2 public constructors of the DbConnectionInfo are
public DbConnectionInfo(string connectionName)
connectionName: The name of the connection string in the application configuration.
and
public DbConnectionInfo(string connectionString, string providerInvariantName)
connectionString: The connection string to use for the connection.
providerInvariantName: The name of the provider to use for the connection. Use 'System.Data.SqlClient' for SQL Server.
I see you have the connection string, but have no idea how you can get the providerInvariantName.
UPDATE: I didn't find a good "official" way of taking the needed information, so I've ended using a hack with accessing internal members via reflection, but still IMO it's a quite more safer than what you have used:
var internalContext = context.GetType().GetProperty("InternalContext", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(context);
var providerName = (string)internalContext.GetType().GetProperty("ProviderName").GetValue(internalContext);
var conf = new Workshop.Migrations.Configuration();
conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(connectionString, providerName);

SQL Change Tracking and Microsoft Sync Framework

I'm kind of new with databases and SQL and I'm struggling trying to understand how SQL Change Tracking and Microsoft Sync Framework work together.
I couldn't find some clear examples about how to sync databases with Microsoft Sync Framework but hopefully I found this site, modified the code and got syncing working on my two databases, here is the code I got:
// Server connection
using (SqlConnection serverConn = new SqlConnection(serverConnectionString))
{
if (serverConn.State == ConnectionState.Closed)
serverConn.Open();
// Client connection
using (SqlConnection clientConn = new SqlConnection(clientConnectionString))
{
if (clientConn.State == ConnectionState.Closed)
clientConn.Open();
const string scopeName = "DifferentPKScope";
// Provision Server
var serverProvision = new SqlSyncScopeProvisioning(serverConn);
if (!serverProvision.ScopeExists(scopeName))
{
var serverScopeDesc = new DbSyncScopeDescription(scopeName);
var serverTableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable(table, serverConn);
// Add the table to the descriptor
serverScopeDesc.Tables.Add(serverTableDesc);
serverProvision.PopulateFromScopeDescription(serverScopeDesc);
serverProvision.Apply();
}
// Provision Client
var clientProvision = new SqlSyncScopeProvisioning(clientConn);
if (!clientProvision.ScopeExists(scopeName))
{
var clientScopeDesc = new DbSyncScopeDescription(scopeName);
var clientTableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable(table, clientConn);
// Add the table to the descriptor
clientScopeDesc.Tables.Add(clientTableDesc);
clientProvision.PopulateFromScopeDescription(clientScopeDesc);
clientProvision.SetCreateTrackingTableDefault(DbSyncCreationOption.CreateOrUseExisting);
clientProvision.Apply();
}
// Create the sync orchestrator
var syncOrchestrator = new SyncOrchestrator();
// Setup providers
var localProvider = new SqlSyncProvider(scopeName, clientConn);
var remoteProvider = new SqlSyncProvider(scopeName, serverConn);
syncOrchestrator.LocalProvider = localProvider;
syncOrchestrator.RemoteProvider = remoteProvider;
// Set the direction of sync session
syncOrchestrator.Direction = direction;
// Execute the synchronization process
return syncOrchestrator.Synchronize();
}
}
So on this way any changes are synchronized between my two databases. But I wanted a way for my C# app to automatically synchronize both databases when something changes so I found something called Change Tracking here. I downloaded the example code that provides a SynchronizationHelper that also creates tables in my databases called "{TableName}_tracking". This is another table that tracks the changes and indeed it does, whenever I change something in my database the _tracking is updated with the elements I changed, added or removed. Change Tracking doesn't automatically synchronize my databases, it just keeps track of the changes in them, what's the purpose of this?
With the first code, synchronization works but no _tracking table is created, does it just synchronize everything in the table no matter what changed? If that's the case, for big databases I should be using Change Tracking?
Maybe this is something trivial but I have been googling and testing a lot of code but I can't find a clear answer.
When you install Sync Framework, it comes with a help file that includes several walkthroughs of synchronizing databases. the first link you referred to and the second uses the same sync provider and they both have tracking tables. Sync Framework supports using the built-in SQL Change Tracking feature or using a custom-one that Sync Framework creates by itself (the _tracking).
Sync Framework sits outside of your database and you need to invoke it in order to fire the synchronization. Change Tracking is what it says it is- tracking changes.
if you want your databases to do the sync, you might want to check SQL Replication instead.

Ado.net firebird commit insert new lines to table

I'm adding new lines to a database for our company's "order list" for each order created, using the firebird ado.net client. The code i've written works fine for listing items, but inserting new ones doesn't appear elsewhere (e.g. in flamerobin). What I think is happening is that the transaction isn't being committed, seeing as it's recognised within my code (can't add duplicate values).
Code:
using (FbConnection fbCon = new FbConnection)
{
fbCon.Open();
***command w/ parameterised command string***
using (FbTransaction fbTrans = fbCon.BeginTransaction())
using FbCommand orderCommand = new FbCommand(cmdString, fbCon, fbTrans)
{
***Adding parameters and values***
try
{
int recordsAffected = orderCommand.ExecuteNonQuery();
fbTrans.Commit();
}
catch (FbException E)
{
fbTrans.Rollback();
fbCon.Close();
throw E
}
}
recordsAffected returns 1 but I am not able to see the updated values in flamerobin or the db management program. Am i missing something?
If anyone else runs into this problem, it's in Visual Studio's debugging settings. https://visualstudiomagazine.com/blogs/tool-tracker/2012/05/dealing-with-local-databases-or-why-your-updates-dont-stick.aspx explains pretty clearly, but basically VS makes a copy of your database in bin/Debug of your project (Ostensibly to not mess with your data) but if you actually need to use/view the data externally, either link your external application to the debug database (e.g. flamerobin). You may also need to set your project database settings to Copy if Newer if you want your updates to stay, as the default setting copies the database into the folder each time you run your c# app.

linq to entities changing database connection string in code

I am using the devart component dotconnect for postgresql. I have created the site using linq to entities, however, I would like each user to have a seperate database. This means that I need to change the connection string for each person that has logged in. I understand the main part of how to generate a new connection string etc, however, when i pass that as a paramater to the object context object it comes back with the error
"user id keyword not supported, "
if i create a class that generates an entity connection the error message changes to:
"Unable to load the specified metadata resource."
Cannot work out what I have done wrong in these instances.
ok so, as usual, when i posted this question, about 3 minutes later i found the problem. The entity connection string, for general purposes should have a cool little
res://*/
this makes the metadata work. This solves the problem of metadata resource and this works. So to help others who may, like me, have spent development time doing this, i created a class, with a method like so.
public static string getConnString(string database)
{
string connectionstring = "User Id=USER ID HERE;Password=PASSWORD HERE;Host=server;Database="+database+";Persist Security Info=True;Schema=public";
EntityConnectionStringBuilder newconnstring = new EntityConnectionStringBuilder();
newconnstring.Metadata = #"res://*/";
newconnstring.Provider = "Devart.Data.PostgreSql";
newconnstring.ProviderConnectionString = connectionstring;
return newconnstring.ToString();
}
then create a constructor like so
dataEntities data = new dataEntities(databaseConnection.getConnString(INSERTDBNAMEHERE);
Then we can reference that in the same way as a usual linq statement. Simples!!

Categories

Resources