Is it possible using the .Net driver to:
Check whether a certain Mongo instance is already a part of a replica
Create a replica if not
Add \ Remove a node from an existing replica
Thanks!
Yes. But it's much easier to do these things in the mongo shell because it has helper functions defined already. Check out the replica set tutorials. For each step using some function in the mongo shell, for example rs.initiate() or rs.add(), you can see the code for the function by entering the function name with parentheses at the shell prompt:
> rs.initiate
function (c) { return db._adminCommand({ replSetInitiate: c }); }
> rs.add
function (hostport, arb) {
var cfg = hostport;
var local = db.getSisterDB("local");
assert(local.system.replset.count() <= 1, "error: local.system.replset has unexpected contents");
var c = local.system.replset.findOne();
assert(c, "no config object retrievable from local.system.replset");
c.version++;
var max = 0;
for (var i in c.members)
if (c.members[i]._id > max) max = c.members[i]._id;
if (isString(hostport)) {
cfg = { _id: max + 1, host: hostport };
if (arb)
cfg.arbiterOnly = true;
}
if (cfg._id == null){
cfg._id = max+1;
}
c.members.push(cfg);
return this._runCmd({ replSetReconfig: c });
}
The code may be slightly different in your shell because of shell version; my shell is 3.0.1. You can use this code as a guide for writing your own versions of these functions in C#. You will use the RunCommand method to run the commands like replSetReconfig and replSetInitiate on the server(s).
Related
Due to the lack of examples in C#, I can't get reflection in gRPC\Protobuf working. One application would be supplying a version of all interfaces and messages.
syntax = "proto3";
import "google/protobuf/descriptor.proto";
option csharp_namespace = "Addressbook.Services";
extend google.protobuf.FileOptions {
string version = 50000;
}
option (version) = "1.2.3.0";
service AddressBookService {
...
Is it possible that a client parses the connected server with reflection? Is the version supplied to all interfaces and messages? How to I do that in C#?
Thanks for any help...
If I understand the question correctly, you want to see if client and server are in sync with all .proto files.
Here's a solution that will have to be tweaked but otherwise will work.
This only works if server has reflection enabled - maybe not an option everywhere.
In ServiceName.Descriptor.File ServiceName refers to class generated by protoc.
using var channel = _channelFactory.GetChannel();
var client = new ServerReflection.ServerReflectionClient(channel);
using var call = client.ServerReflectionInfo();
// To get all service names on the server, you can use this:
// await call.RequestStream.WriteAsync(new ServerReflectionRequest{ ListServices = ""});
// await call.ResponseStream.MoveNext(CancellationToken.None);
// foreach (var serviceResponse in call.ResponseStream.Current.ListServicesResponse.Service)
// {
// _logger.LogInformation("Service name on server: {ServiceName}", serviceResponse.Name);
// }
await call.RequestStream.WriteAsync(new ServerReflectionRequest{FileContainingSymbol = "name.space.service_name"});
await call.ResponseStream.MoveNext(CancellationToken.None);
var descriptorResponse = call.ResponseStream.Current;
// This list has to be reversed. Check `.BuildFromByteStrings` docstring for more info.
var fileDescriptors = FileDescriptor.BuildFromByteStrings(descriptorResponse.FileDescriptorResponse.FileDescriptorProto.Reverse());
var localDescriptor = ServiceName.Descriptor.File;
var relevantFileDescriptor = fileDescriptors.Single(descriptor => descriptor.Name == localDescriptor.Name);
if (relevantFileDescriptor.SerializedData != localDescriptor.SerializedData)
{
_logger.LogWarning("Remote .proto differs from local copy. Please update.");
}
else
{
_logger.LogInformation(".proto definition matches between client and server.");
}
await call.RequestStream.CompleteAsync();
I'm using MongoDB 4.0.8 with C# driver 2.8.1 and I'm trying to implement Transactions in my project.
I copy-pasted the following code sample:
static async Task<bool> UpdateProducts()
{
//Create client connection to our MongoDB database
var client = new MongoClient(MongoDBConnectionString);
//Create a session object that is used when leveraging transactions
var session = client.StartSession();
//Create the collection object that represents the "products" collection
var products = session.Client.GetDatabase("MongoDBStore").GetCollection<Product>("products");
//Clean up the collection if there is data in there
products.Database.DropCollection("products");
//Create some sample data
var TV = new Product { Description = "Television", SKU = 4001, Price = 2000 };
var Book = new Product { Description = "A funny book", SKU = 43221, Price = 19.99 };
var DogBowl = new Product { Description = "Bowl for Fido", SKU = 123, Price = 40.00 };
//Begin transaction
session.StartTransaction(new TransactionOptions(
readConcern: ReadConcern.Snapshot,
writeConcern: WriteConcern.WMajority));
try
{
//Insert the sample data
await products.InsertOneAsync(session, TV);
await products.InsertOneAsync(session, Book);
await products.InsertOneAsync(session, DogBowl);
var filter = new FilterDefinitionBuilder<Product>().Empty;
var results = await products.Find(filter).ToListAsync();
//Increase all the prices by 10% for all products
var update = new UpdateDefinitionBuilder<Product>().Mul<Double>(r => r.Price, 1.1);
await products.UpdateManyAsync(session, filter, update); //,options);
//Made it here without error? Let's commit the transaction
session.CommitTransaction();
//Let's print the new results to the console
Console.WriteLine("Original Prices:\n");
results = await products.Find<Product>(filter).ToListAsync();
foreach (Product d in results)
{
Console.WriteLine(String.Format("Product Name: {0}\tPrice: {1:0.00}", d.Description, d.Price));
}
}
catch (Exception e)
{
Console.WriteLine("Error writing to MongoDB: " + e.Message);
session.AbortTransaction();
}
return true;
}
But in the first Insert command, I'm getting this error:
Command insert failed:
Transaction numbers are only allowed on a replica set member or mongos.
The Documentation says that:
Starting in version 4.0, MongoDB provides the ability to perform multi-document transactions against replica sets.
I don't have replicas in my project, I have only one database instance which is my primary one. If there a solution or a work-around I can use to implement Transactions? I have methods that update more than one collection and I really think it could save me time to use it.
like the documentation says, transactions only work with replica sets. so you need to run your mongodb server as single node replica set. to achieve that, do the following steps...
step 1:
stop the mongodb server.
step 2:
add the replication setting to your mongod.cfg file. here's my own as an example
storage:
dbPath: C:\DATA
directoryPerDB: true
journal:
enabled: true
systemLog:
destination: file
logAppend: true
path: C:\DATA\log\mongod.log
net:
port: 27017
bindIp: 127.0.0.1
replication:
replSetName: MyRepSet
step 3: open up a mongodb shell and issue the following command to initiate the replica set.
rs.initiate()
step 4: restart mongod
on a side-note, if you'd like to write cleaner, more convenient transaction code like the following, check out my library MongoDB.Entities
using (var TN = new Transaction())
{
var author = new Author { Name = "one" };
TN.Save(author);
TN.Delete<Book>(book.ID);
TN.Commit();
}
I'm using IronPython v 2.7.8.1 in VS 2017. I've installed Python27, 36 and 37. I've tried switching between the various environments in VS. I've tried adding the search paths to the libraries of these installs. The python code will work if I run it in the interpreter. Trying to run the same code in VS throws: "Microsoft.Scripting.SyntaxErrorException: unexpected token ',' ". If I test a python script that doesn't include imports it will work? Is there a specific way python has to be installed to work IronPython? This is the C# Code:
class CallPython
{
public void PatchParameter(string parameter)
{
var FilePath = (#"C:\Users\Pac\Downloads\MvcAuth\MvcAuth\TwurlPy\TwurlPy\SendDirectMsg.py");
var engine = Python.CreateEngine(); // Extract Python language engine from their grasp
ICollection<string> searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib");
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib\site-packages");
engine.SetSearchPaths(searchPaths);
var scope = engine.CreateScope(); // Introduce Python namespace
(scope)
var d = new Dictionary<string, object>
{
{ "text", text},
{ "userID", userID},
};
// Add some sample parameters. Notice that there is no need in
// specifically setting the object type, interpreter will do that part for us
// in the script properly with high probability
scope.SetVariable("params", d); // This will be the name of the
// dictionary in python script, initialized with previously created .NET
// Dictionary
ScriptSource source = engine.CreateScriptSourceFromFile(FilePath);
// Load the script
object result = source.Execute(scope);
parameter = scope.GetVariable<string>("parameter"); // To get the
// finally set variable 'parameter' from the python script
return;
}
}
This is the Python script. If I comment out the import statements it works using IronPython, but of course I need them...
import twitter
import requests
import sys
parameter = "test"
def SendDM(text, userID, access_token, access_secret):
consumer_key = 'YkopsCQjEXccccccccccccccccccZvA9yy'
consumer_secret = 'TQVCoccccccccccccccccccct7y8VfmE'
access_token_key = access_token
access_token_secret = access_secret
api = twitter.Api(
consumer_key=consumer_key,
consumer_secret=consumer_secret,
access_token_key=access_token_key,
access_token_secret=access_token_secret)
send_msg = api.PostDirectMessage(text, user_id=userID)
print (send_msg)
return
SendDM(text, userID, access_token, access_secret)
In my deployment tool I want to check programmatically whether there are updates available for the project which should be deployed somewhere.
The tool should not update the workspace on itself, it's just supposed to warn the user that there is a new version available in the TFS repository.
I tried the following prototypic function to no avail:
public static bool AreWorkspaceAndRepositoryInSync()
{
const string tfsUrl = "http://mytfs:8080/tfs/MyCollection";
const string tfsPath = #"$/Main/MyProject";
const string wsPath = #"C:\DEV\MyProject";
var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsUrl));
var vcs = projectCollection.GetService<VersionControlServer>();
var workspaceItems = vcs.GetItems(wsPath, RecursionType.Full);
var repositoryItems = vcs.GetItems(tfsPath, VersionSpec.Latest, RecursionType.Full);
var wsNewest = workspaceItems.Items.Max(i => i.CheckinDate);
var repoNewest = repositoryItems.Items.Max(i => i.CheckinDate);
if (wsNewest != repoNewest)
return false;
return true;
}
This function always returns true. I suspect that VersionControlServer.GetItems(...) always retrieves the Items from the server, hence my comparision actually compares two identical list.
Is there a way to get the local workspace Items? Or is there any other way to achieve something similar?
I wouldn't recommend trying to compute it yourself. The best way to do this is to run tf get /preview. That will tell you if there is anything to do but not actually update the workspace. Using the client OM, call Get with VersionSpec.Latest and GetOptions.Preview.
I'd like to create a small application that can collect system information (Win32_blablabla) using WinRM as opposed to WMI. How can i do that from C#?
The main goal is to use WS-Man (WinRm) as opposed to DCOM (WMI).
I guess the easiest way would be to use WSMAN automation. Reference wsmauto.dll from windwos\system32 in your project:
then, code below should work for you. API description is here: msdn: WinRM C++ API
IWSMan wsman = new WSManClass();
IWSManConnectionOptions options = (IWSManConnectionOptions)wsman.CreateConnectionOptions();
if (options != null)
{
try
{
// options.UserName = ???;
// options.Password = ???;
IWSManSession session = (IWSManSession)wsman.CreateSession("http://<your_server_name>/wsman", 0, options);
if (session != null)
{
try
{
// retrieve the Win32_Service xml representation
var reply = session.Get("http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service?Name=winmgmt", 0);
// parse xml and dump service name and description
var doc = new XmlDocument();
doc.LoadXml(reply);
foreach (var elementName in new string[] { "p:Caption", "p:Description" })
{
var node = doc.GetElementsByTagName(elementName)[0];
if (node != null) Console.WriteLine(node.InnerText);
}
}
finally
{
Marshal.ReleaseComObject(session);
}
}
}
finally
{
Marshal.ReleaseComObject(options);
}
}
hope this helps, regards
I've got an article that describes an easy way to run Powershell through WinRM from .NET at http://getthinktank.com/2015/06/22/naos-winrm-windows-remote-management-through-net/.
The code is in a single file if you want to just copy it and it's also a NuGet package that includes the reference to System.Management.Automation.
It auto manages trusted hosts, can run script blocks, and also send files (which isn't really supported but I created a work around). The returns are always the raw objects from Powershell.
// this is the entrypoint to interact with the system (interfaced for testing).
var machineManager = new MachineManager(
"10.0.0.1",
"Administrator",
MachineManager.ConvertStringToSecureString("xxx"),
true);
// will perform a user initiated reboot.
machineManager.Reboot();
// can run random script blocks WITH parameters.
var fileObjects = machineManager.RunScript(
"{ param($path) ls $path }",
new[] { #"C:\PathToList" });
// can transfer files to the remote server (over WinRM's protocol!).
var localFilePath = #"D:\Temp\BigFileLocal.nupkg";
var fileBytes = File.ReadAllBytes(localFilePath);
var remoteFilePath = #"D:\Temp\BigFileRemote.nupkg";
machineManager.SendFile(remoteFilePath, fileBytes);
Hope this helps, I've been using this for a while with my automated deployments. Please leave comments if you find issues.
I would like to note that this shows an interop error by default in Visual Studio 2010.
c.f. http://blogs.msdn.com/b/mshneer/archive/2009/12/07/interop-type-xxx-cannot-be-embedded-use-the-applicable-interface-instead.aspx
There appear to be two ways to solve this. This first is documented in the article listed above and appears to be the correct way to handle the problem. The pertinent changes for this example is:
WSMan wsManObject = new WSMan();
This is in lieu of IWSMan wsman = new WSManClass(); which will throw the error.
The second resolution is to go to the VS2010—>Solution Explorer—>Solution—>Project—>References and select WSManAutomation. Right click or hit Alt-Enter to access the properties. Change the value of the "Embed Interop Types" property of the wsmauto reference.