I'm having a problem with lots of connections being opened to the mongo db.
The readme on the Github page for the C# driver gives the following code:
using MongoDB.Bson;
using MongoDB.Driver;
var client = new MongoClient("mongodb://localhost:27017");
var server = client.GetServer();
var database = server.GetDatabase("foo");
var collection = database.GetCollection("bar");
collection.Insert(new BsonDocument("Name", "Jack"));
foreach(var document in collection.FindAll())
{
Console.WriteLine(document["Name"]);
}
At what point does the driver open the connection to the server? Is it at the GetServer() method or is it the Insert() method?
I know that we should have a static object for the client, but should we also have a static object for the server and database as well?
Late answer... but the server connection is created at this point:
var client = new MongoClient("mongodb://localhost:27017");
Everything else is just getting references for various objects.
See: http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-csharp-driver/
While using the latest MongoDB drivers for C#, the connection happens at the actual database operation. For eg. db.Collection.Find() or at db.collection.InsertOne().
{
//code for initialization
//for localhost connection there is no need to specify the db server url and port.
var client = new MongoClient("mongodb://localhost:27017/");
var db = client.GetDatabase("TestDb");
Collection = db.GetCollection<T>("testCollection");
}
//Code for db operations
{
//The connection happens here.
var collection = db.Collection;
//Your find operation
var model = collection.Find(Builders<Model>.Filter.Empty).ToList();
//Your insert operation
collection.InsertOne(Model);
}
I found this out after I stopped my mongod server and debugged the code with breakpoint. Initialization happened smoothly but error was thrown at db operation.
Hope this helps.
Related
I'm on a project where I need to manage connections to both a MongoDB Instance and a PostgreSQL instance.
My current idea is to make a custom type that will contain an Arc<Mutex<pgConnection>> and an Arc<Mutex<MongoConnection>> in a struct that itself is within an Arc which would be passed to the Actix Web app_data initialization function.
e.g.
// this is pseudo-code, kinda
type DbPoolPG = r2d2::Pool<ConnectionManager<PostgreSQL>>;
// wont be an r2d2 pool, MongoDB official drivers handle pooling automatically
type DbPoolMongo = r2d2::Pool<ConnectionManager<MongoDB>>;
struct DatabseConnections {
pg: Arc<Mutex<DbPoolPG>>;
mongo: Arc<Mutex<DbPoolMongo>>;
}
#[actix_web::main]
async fn main() -> io::Result<()> {
// Create connection pools
let PostGresPool = r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool.");
let MongoPool = mongo.create_connection()
let connections = DatabaseConnections {
pg: Arc::new(Mutex::new(PostGresPool))
mongo: Arc::new(Mutex::new(MongoPool))
}
// Start HTTP server
HttpServer::new(move || {
App::new().app_data(web::Data::new(Arc::new(connections)))
.resource("/{name}", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
The idea seems a bit too simple to actually work though, Does anyone else have any ideas?
nvm, you can just call app_data twice as long as the types are different. then access them by calling the types
I have a dockerized .net core container and am trying to query a MongoDB database. I have a REST API that is called to query the database from the .net core container. It seems as if I am able to establish a connection to the container when the instance of the service is created:
private readonly IMongoCollection<DbObject> _dbObjects;
public TaxService(IDbConfig dbConfig)
{
Console.Out.WriteLine("got here: " + dbConfig.ConnectionString);
MongoClient client = new MongoClient(dbConfig.ConnectionString);
IMongoDatabase database = client.GetDatabase(dbConfig.DatabaseName);
_dbObjects = database.GetCollection<DbObject>(dbConfig.SalesTaxCollectionName);
Console.Out.WriteLine("db initialized");
}
Here is the dbConfig object:
"DbConfig": {
"SalesTaxCollectionName": "ExampleCollection",
"ConnectionString": "mongodb://mongo:27017",
"DatabaseName": "ExampleDb"
}
It gets through this code, but when the database is actually queried, a PlatformNotSupportedException is thrown:
public DbObject GetByValue(string value)
{
Console.Out.WriteLine("querying db");
List<DbObject> matches = _dbObjects.Find(dbObject => dbObject.Value.Equals(value)).ToList();
Console.Out.WriteLine("successfully queried"); // Does not reach this point
if (matches.Any())
{
return matches.First();
}
else
{
throw new ArgumentException($"No values matched");
}
}
I have put the rest of the files in gists for convenience:
docker-compose.yml: https://gist.github.com/MinhazMurks/1fbb47afd360bbac48df45b1f0609e33
Dockerfile: https://gist.github.com/MinhazMurks/bb2b7f76d28894a81136d940b5997165
InitExampleDb.js: https://gist.github.com/MinhazMurks/9aae03daceee1e689c4e821419966f41
Full-Log: https://gist.github.com/MinhazMurks/0de9cd822fcf2930065527191127c83b
Any insight would be appreciated!
You can try to set the following line in the host file:
(It is located here in case of Windows: C:\Windows\System32\drivers\etc\hosts)
127.0.0.1 mongo
After this try connect using RoboMongo. In the Address textbox insert mongo and try to connect to it.
How do you call the Ping command with the new C# driver 2.0?
In the old driver it was available via Server.Ping()? Also, Is there a way to find out if the server is running/responding without running the actual query?
Using mongoClient.Cluster.Description.State doesn't help because it still gave the disconnected state even after the mongo server started responding.
You can check the cluster's status using its Description property:
var state = _client.Cluster.Description.State
If you want a specific server out of that cluster you can use the Servers property:
var state = _client.Cluster.Description.Servers.Single().State;
This worked for me on both c# driver 2 and 1
int count = 0;
var client = new MongoClient(connection);
// This while loop is to allow us to detect if we are connected to the MongoDB server
// if we are then we miss the execption but after 5 seconds and the connection has not
// been made we throw the execption.
while (client.Cluster.Description.State.ToString() == "Disconnected") {
Thread.Sleep(100);
if (count++ >= 50) {
throw new Exception("Unable to connect to the database. Please make sure that "
+ client.Settings.Server.Host + " is online");
}
}
As #i3arnon's answer I can tell it was reliable for me in this way:
var server = client.Cluster.Description.Servers.FirstOrDefault();
var serverState = ServerState.Disconnected;
if (server != null) serverState = server.State;
or in new versions of .Net
var serverState = client.Cluster.Description.Servers.FirstOrDefault()?.State
?? ServerState.Disconnected;
But if you realy want to run a ping command you can do it like this:
var command = new CommandDocument("ping", 1);
try
{
db.RunCommand<BsonDocument>(command);
}
catch (Exception ex)
{
// ping failed
}
We have two different query strategies that we'd ideally like to operate in conjunction on our site without opening redundant connections. One strategy uses the enterprise library to pull Database objects and Execute_____(DbCommand)s on the Database, without directly selecting any sort of connection. Effectively like this:
Database db = DatabaseFactory.CreateDatabase();
DbCommand q = db.GetStoredProcCommand("SomeProc");
using (IDataReader r = db.ExecuteReader(q))
{
List<RecordType> rv = new List<RecordType>();
while (r.Read())
{
rv.Add(RecordType.CreateFromReader(r));
}
return rv;
}
The other, newer strategy, uses a library that asks for an IDbConnection, which it Close()es immediately after execution. So, we do something like this:
DbConnection c = DatabaseFactory.CreateDatabase().CreateConnection();
using (QueryBuilder qb = new QueryBuilder(c))
{
return qb.Find<RecordType>(ConditionCollection);
}
But, the connection returned by CreateConnection() isn't the same one used by the Database.ExecuteReader(), which is apparently left open between queries. So, when we call a data access method using the new strategy after one using the old strategy inside a TransactionScope, it causes unnecessary promotion -- promotion that I'm not sure we have the ability to configure for (we don't have administrative access to the SQL Server).
Before we go down the path of modifying the query-builder-library to work with the Enterprise Library's Database objects ... Is there a way to retrieve, if existent, the open connection last used by one of the Database.Execute_______() methods?
Yes, you can get the connection associated with a transaction. Enterprise Library internally manages a collection of transactions and the associated database connections so if you are in a transaction you can retrieve the connection associated with a database using the static TransactionScopeConnections.GetConnection method:
using (var scope = new TransactionScope())
{
IEnumerable<RecordType> records = GetRecordTypes();
Database db = DatabaseFactory.CreateDatabase();
DbConnection connection = TransactionScopeConnections.GetConnection(db).Connection;
}
public static IEnumerable<RecordType> GetRecordTypes()
{
Database db = DatabaseFactory.CreateDatabase();
DbCommand q = db.GetStoredProcCommand("GetLogEntries");
using (IDataReader r = db.ExecuteReader(q))
{
List<RecordType> rv = new List<RecordType>();
while (r.Read())
{
rv.Add(RecordType.CreateFromReader(r));
}
return rv;
}
}
I have a long running operation that inserts thousands of sets of entries, each time a set is inserted using the code below.
After a while of this code running, the collection.Update() method freezes (does not return) and the entire process grinds to a halt.
Can't find any reasonable explanation for this anywhere.
I've looked at the mongod logs, nothing unusual, it just stops receiving requests from this process.
Mongo version: 2.4.1, C# driver version: 1.8.0
using (_mongoServer.RequestStart(_database))
{
var collection = GetCollection<BsonDocument>(collectionName);
// Iterate over all records
foreach (var recordToInsert in recordsDescriptorsToInsert)
{
var query = new QueryDocument();
var update = new UpdateBuilder();
foreach (var property in recordToInsert)
{
var field = property.Item1;
var value = BsonValue.Create(property.Item2);
if (keys.Contains(field))
query.Add(field, value);
update.Set(field, value);
}
collection.Update(query, update, UpdateFlags.Upsert); // ** NEVER RETURNS **
}
}
This is may related to this: CSHARP-717
It was fixed for driver 1.8.1