MongoDB run command with json return - c#

My overall goal is to run a query that shows all the collections with the amount of data / counts and so on.
I found the following command/query and try to run it like this:
MongoClient client = new MongoClient(server);
var db = client.GetDatabase(database);
const string mongoQueryA = "var collectionNames = db.getCollectionNames(), stats = []; " +
"collectionNames.forEach(function(n) { stats.push(db[n].stats()); }); " +
"stats = stats.sort(function(a, b) { return b['size'] - a['size']; }); ";
var command = new JsonCommand<BsonDocument>(mongoQueryA);
var test = db.RunCommand<BsonDocument>(command);
And the code fails here. Exceptions:
JSON reader was expecting a value but found 'var'.
My understanding is that this shoul be run as command?
Running the query in Robot T works as expected. Bonus the plan was to return the data in the following format ( Based on Json from running query manualy in Robot T)
class MongoCollectionInfo
{
public string ns { get; set; }
public long size { get; set; }
public long count { get; set; }
public long avgObjSize { get; set; }
public long storageSize { get; set; }
public long nindexes { get; set; }
public long totalIndexSize { get; set; }
}

What you're trying to achieve can be done purely in C# as it looks like what you've attempted to do is send javascript to the server to be executed which is not how commands work.
To start off with we'll need to get a list of all the collections within a database inside the MongoDB instance.
var client = new MongoClient();
var db = client.GetDatabase("test");
var collectionNames = await (await db.ListCollectionNamesAsync()).ToListAsync();
once we've got the collection names (collectionNames) we can then go ask the database for the stats on that collection by issue a command.
var allCollStats = new List<BsonDocument>(collectionNames.Count);
foreach (var name in collectionNames)
{
var collStatsResult = await db.RunCommandAsync(new BsonDocumentCommand<BsonDocument>(new BsonDocument("collStats", name)));
allCollStats.Add(collStatsResult);
}
The allCollStats then will hold all the stats for the collections and we can then use it as we see fit
foreach (var collStats in allCollStats)
{
Console.WriteLine($"ns: {collStats["ns"]}, size: {collStats["size"]}");
}
// ns: test.test, size: 117
// ns: test.people, size: 5092
Also, if you wanted to use a typed result instead of a BsonDocument you can pass this in as the generic agrument to the command.
var collStatsResult = await db.RunCommandAsync(new BsonDocumentCommand<MongoCollectionInfo>(new BsonDocument("collStats", name)));
Console.WriteLine($"ns: {collStatsResult .ns}, size: {collStatsResult .size}");

Related

C# + Copy JSON Data

I have the below JSON data stored in one of my fields.
I am want to copy this data in another new record however the guid's mentioned are not copied exactly as it is. I have a dictionary created in my code where these guid's are part of the key and value guid's against it must be updated in the new json.
I have written the code for it However just want to know if there is a better way of doing it.
{ "masterTable" :"tablename",
"selectedColumns":"f8a96c63-3d0d-ed11-82e5-002248189771,
dca1116d-410d-ed11-82e5- 002248189771"}
newtoOldGuids = {"4d6048ea-c40e-ed11-82e5-00224818919f|f8a96c63-3d0d-ed11-82e5-002248189771", "8a719554-9b0c-ed11-82e5-00224818919f"},
{"4d6048ea-c40e-ed11-82e5-00224818919f|dca1116d-410d-ed11-82e5- 002248189771","70e62e29-9b0c-ed11-82e5-00224818919f"}
So the new JSON should look like below -
{ "masterTable" :"tablename",
"selectedColumns":"8a719554-9b0c-ed11-82e5-00224818919f,
70e62e29-9b0c-ed11-82e5-00224818919f"}
Below is my C# code -
public class Filters
{
public string MasterTable { get; set; }
public string SelectedColumns { get; set; }
}
var filterJsonObj = JsonConvert.DeserializeObject<Filters>(filterJson);
List<string> colsList = filterJsonObj.SelectedColumns.Split(',').ToList();
List<string> newColsList = new List<string>();
foreach (var i in colsList)
{
var newguid = newtoOldGuids.Where(kvp => kvp.Key.Contains(i)).Select(kvp => kvp.Value).ToList();
newColsList.Add(newguid[0].ToString());
}
filterJsonObj.SelectedColumns = newColsList.Count > 0 ? string.Join(",", newColsList) : string.Empty;
return JsonConvert.SerializeObject(filterJsonObj);

Deserialize JSON to list with unknown object name in C#

I want to deserialize following JSON.
The problem is that the objects "ANDE" & "DAR" can change.
Means the objects are unknown and change depending on the JSON i wanna deserialize.
About 8000 different objects (ANDE, DAR, ...) need to be deserialized.
{"ANDE":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]},
"DAR":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]}}
I get the data by HTTP response and want to put into a List:
HttpResponseMessage response = client.GetAsync(API_PATH).GetAwaiter().GetResult();
List historicalDataList = response.Content.ReadAsAsync<List<HistoricalDataResponse>>().GetAwaiter().GetResult();
The HistoricalDataResponse class looks like:
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}
How can i deserialize this kind of JSON with unknown objects in C#?
Any help is highly appreciated.
Then you should use a dynamic variable:
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
note that as in dynamic objects, properties are determined after being assigned in runtime, so you will not get a drop down menu in design time, and also as its properties are unknown in design time, and property you test in design time even if its not correct, you wont get an error, and you will get the error in runtime when it is assigned.
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
try
{
var a = ReturnValue.ANDE; // will work if it has ANDE property.
// do what you would do with ANDE
}
catch{}
try
{
var a = ReturnValue.DAR; // will work if it has DAR property.
// do what you would do with DAR
}
catch{}
Use a dictionary with string as key type :
void Main()
{
var client = new HttpClient();
HttpResponseMessage response = client.GetAsync("url").GetAwaiter().GetResult();
var json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<Dictionary<string, DateResponse>>(json);
foreach (var element in result)
{
var key = element.Key; // ANDE
foreach (var item in element.Value.Chart)
{
var date = item.date;
var minute = item.minute;
}
}
}
public class DateResponse{
public List<HistoricalDataResponse> Chart { get; set; }
}
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}

Dealing with CimObjects with PowerShell inside C#

I have a snippet that executes a PowerShell script
using (var ps = PowerShell.Create())
{
ps.AddScript("function Test() { return Get-Disk -Number 0 } ");
ps.Invoke();
ps.AddCommand("Test");
var results = ps.Invoke();
var disk = results.First();
MyDisk myDisk = // do something to convert disk to myDisk
}
Debuggin, it get his inside disk:
How I'm supposed to deal with this object (CimObject)? I would like to get the values from the "Name" and "Number" properties.
Just to clarify, the object I'm trying to deal is the same type as this (run into PowerShell as admin)
PS C:\windows\system32> $disk = Get-Disk -Number 0
PS C:\windows\system32> $disk.GetType();
How do I interact with this?
Thank you!
I don't think there is any easy way to convert PowerShell output to an easier to handle format. You need to 'manually' pull out the properties you want. For example, you can get the 'AllocatedSize' value like this:
var allocatedSize = results.First().Members["AllocatedSize"].Value;
If you want your own types based on these values, then you can do something like this:
Define your type (change the properties to suit the ones you want):
public class MyDisk
{
public long AllocatedSize { get; set; }
public string FriendlyName { get; set; }
public bool IsBoot { get; set; }
public int Number { get; set; }
}
Add a helper method that does the conversion:
private static MyDisk ConvertToMyDisk(PSMemberInfoCollection<PSMemberInfo> item)
{
return new MyDisk
{
AllocatedSize = long.Parse(item["AllocatedSize")].Value.ToString()),
FriendlyName = item["FriendlyName"].Value.ToString(),
IsBoot = bool.Parse(item["IsBoot"].Value.ToString()),
Number = int.Parse(item["Number"].Value.ToString())
};
}
You can then convert the return values to your own type with some basic LINQ:
List<MyDisk> myDisks = results.Select(d => ConvertToMyDisk(d.Members)).ToList();

How can I use the Entity Framework Core capabilities to change my database?

I am building an application on top of Entity Framework Core and I want to, sort of, apply a migration at runtime.
My intended approach is to have the current database model in memory and create a new model, then calculate the difference between the two models using IMigrationsModelDiffer.GetDifferences().
From there, instead of printing the differences into a Migration class, I want to create the MigrationCommands directly and apply those commands to my database.
The above sounds fairly straightforward but I'm having a lot of issues with the Dependency Injection system.
This is the code I have right now:
static DbContextOptions GetOptions(IModel model)
{
var builder = new DbContextOptionsBuilder();
builder
.UseSqlServer(connStr)
.UseModel(model);
return builder.Options;
}
class Test1ModelAEntity
{
public int Id { get; set; }
public string StrProp { get; set; }
}
static void Main(string[] args)
{
var sqlServerServices = new ServiceCollection()
.AddEntityFrameworkSqlServer()
.BuildServiceProvider();
var conventions = new ConventionSet();
sqlServerServices.GetRequiredService<IConventionSetBuilder>().AddConventions(conventions);
var emptyModelBuilder = new ModelBuilder(conventions);
var emptyModel = emptyModelBuilder.Model;
var test1ModelBuilder = new ModelBuilder(conventions);
test1ModelBuilder.Entity<Test1ModelAEntity>()
.ToTable("ModelA");
var test1Model = test1ModelBuilder.Model;
using (TestContext ctx = new TestContext(GetOptions(emptyModel)))
{
var migrationServices = new ServiceCollection()
.AddDbContextDesignTimeServices(ctx)
.AddEntityFrameworkSqlServer()
.BuildServiceProvider();
var operations = migrationServices.GetRequiredService<IMigrationsModelDiffer>().GetDifferences(emptyModel, test1Model);
var commands = migrationServices.GetRequiredService<IMigrationsSqlGenerator>().Generate(operations, test1Model);
var connection = migrationServices.GetRequiredService<IRelationalConnection>();
migrationServices.GetRequiredService<IMigrationCommandExecutor>().ExecuteNonQuery(commands, connection);
}
}
This code throws a NullReferenceException with this stack trace:
at Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping.<>c.<GetRootType>b__10_0(IEntityType t)
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.GetSortedProperties(TableMapping target)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.<Add>d__37.MoveNext()
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.<DiffCollection>d__73`1.MoveNext()
at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.Sort(IEnumerable`1 operations, DiffContext diffContext)
at Sandbox.Program.Main(String[] args) in D:\Sandbox\Program.cs:line 108
I have inspected the source code and it appears that there's an issue with the way EFCore is interpreting my model. I am using EFCore version 2.1 preview 2.
Really I'm mostly trying random configurations on my IServiceCollections because I have no idea how to set this up. I am also trying to stay away from EFCore internal classes but if needed be I may use one or two for the time being.
Is there a way to take advantage of EFCore's built-in capabilities to generate some SQL given a pair of IModels? If so, how do I set up DI to have all the required services?
Thank you for the comments which pointed me in the correct direction.
In summary, I was trying to create my models using an empty convention set. This obviously leads to all sorts of problems as you have to generate the entire model explicitly, which is very complex.
To use the expected convention set I had to get it from my context using ConventionSet.CreateConventionSet. I also had to manually validate my model before being able to use it in queries and insert commands. The rest of the logic is pretty much the same.
Here's my final code including the tests I ran to ensure everything worked as expected:
static DbContextOptions GetOptions(IModel model)
{
var builder = new DbContextOptionsBuilder();
builder
.UseSqlServer(connStr)
.UseModel(model);
return builder.Options;
}
//Test 1
class Test1EntityA
{
public int Id { get; set; }
public string StrProp { get; set; }
}
//Test 2
class Test2EntityA
{
public int Id { get; set; }
public string StrProp { get; set; }
public ICollection<Test2ModelBEntity> Children { get; set; }
}
class Test2EntityB
{
public int Id { get; set; }
public int EntityAId { get; set; }
public Test2EntityA EntityA { get; set; }
}
static void Main(string[] args)
{
var emptyModelBuilder = new ModelBuilder(new ConventionSet());
var emptyModel = emptyModelBuilder.Model;
using (var baseCtx = new TestContext(GetOptions(emptyModel)))
{
//Get all services we need from the base context
var conventions = ConventionSet.CreateConventionSet(baseCtx);
var migrationServices = new ServiceCollection()
.AddDbContextDesignTimeServices(baseCtx)
.AddEntityFrameworkSqlServer()
.BuildServiceProvider();
//Test 1
var test1ModelBuilder = new ModelBuilder(conventions);
test1ModelBuilder.Entity<Test1EntityA>()
.ToTable("EntityA");
var test1Model = test1ModelBuilder.GetInfrastructure().Metadata;
test1Model.Validate();
var operations = migrationServices.GetRequiredService<IMigrationsModelDiffer>().GetDifferences(emptyModel, test1Model);
var commands = migrationServices.GetRequiredService<IMigrationsSqlGenerator>().Generate(operations, test1Model);
var connection = migrationServices.GetRequiredService<IRelationalConnection>();
migrationServices.GetRequiredService<IMigrationCommandExecutor>().ExecuteNonQuery(commands, connection);
using (TestContext ctx = new TestContext(GetOptions(test1Model)))
{
ctx.Set<Test1EntityA>().Add(new Test1EntityA
{
StrProp = "test1",
});
ctx.SaveChanges();
}
//Test 2
var test2ModelBuilder = new ModelBuilder(conventions);
test2ModelBuilder.Entity<Test2EntityA>()
.ToTable("EntityA");
test2ModelBuilder.Entity<Test2EntityB>()
.ToTable("EntityB");
var test2Model = test2ModelBuilder.GetInfrastructure().Metadata;
test2Model.Validate();
operations = migrationServices.GetRequiredService<IMigrationsModelDiffer>().GetDifferences(test1Model, test2Model);
commands = migrationServices.GetRequiredService<IMigrationsSqlGenerator>().Generate(operations, test2Model);
migrationServices.GetRequiredService<IMigrationCommandExecutor>().ExecuteNonQuery(commands, connection);
using (TestContext ctx = new TestContext(GetOptions(test2Model)))
{
var e = new Test2EntityA
{
StrProp = "test2",
};
ctx.Set<Test2EntityA>().Add(e);
ctx.Set<Test2EntityB>().Add(new Test2EntityB
{
EntityA = e,
});
ctx.SaveChanges();
Console.WriteLine(ctx.Set<Test2EntityB>().Where(b => b.EntityA.StrProp == "test2").Count());
}
}
}

MongoDB & Web API - can't understand how to use filters

I am trying to do basic CRUD on a MongoDB using C# Web API. All the examples online are for deprecated methods and I simply can't get the new methods to work.
the places where i need help in the code bellow:
Delete all items in collection - syntax to remove all.
get all items in collection - filter for get all
get by id = syntax to select by id
i've tried many examples online. most are deprecated for previous versions of MongoDB and the official Mongo docs are not helping, i suspect as my code is involving WebAPI classes and the examples are not for that.
thanks for your help!
This is the class:
public class template
{
[BsonId]
public string templateUniqueId { get; set; }
public string outsideClientId { get; set; }
public string ClientId { get; set; }
public string templateFieldsData { get; set; }
public bool isActive { get; set; }
}
And my repository implementation (part of if):
public class templateRepository : ItemplateRepository
{
public MongoClient client;
public IMongoDatabase database;
public IMongoCollection<template> templatesCollection;
public templateRepository()
{
string connectionString = ConfigurationManager.AppSettings["mongoconnection"];
if (string.IsNullOrWhiteSpace(connectionString))
{
connectionString = "mongodb://localhost:27017";
}
client = new MongoClient(connectionString);
database = client.GetDatabase(ConfigurationManager.AppSettings["mongo_personal"]);
templatesCollection = database.GetCollection<template>("templates");
//var persons = await collection.Find(new BsonDocument()).ToListAsync();
// Reset database and add some default entries
FilterDefinition<template> f;
var result = templatesCollection.DeleteMany(f); // this line should remove all items
for (int index = 1; index < 5; index++)
{
template template1 = new template
{
templateUniqueId = string.Format("t{0}", index.ToString()),
outsideClientId= string.Format("t{0}", index.ToString()),
ClientId = string.Format("t{0}", index.ToString()),
isActive = true,
templateFieldsData = "sharon"
};
AddTemplate(template1);
}
}
public void AddTemplate(template templateIn)
{
database.GetCollection<template>("templates").InsertOne(templateIn);
}
public IEnumerable<template> GetAllTemplates()
{
var templates = database.GetCollection<template>("templates").Find({ }); // get all templates
return templates;
}
public template GetTemplate(string id)
{
IMongoQuery query = Query.EQ("_id", id);
return templatesCollection.Find(query).FirstOrDefault();
}
public bool UpdateTemplate(string id, template item)
{
IMongoQuery query = Query.EQ("_id", id);
item.LastModified = DateTime.UtcNow;
IMongoUpdate update = Update
.Set("outsideClientId", item.outsideClientId)
.Set("ClientId", item.ClientId)
.Set("isActive", item.isActive)
.Set("templateFieldsData", item.templateFieldsData);
SafeModeResult result = templatesCollection.Update(query, update);
return result.UpdatedExisting;
}
}
You are using the legacy driver methods, here are some examples for MongoDB C# Driver 2.2:
Delete all items in collection, syntax to remove all.
coll.DeleteMany(FilterDefinition<template>.Empty);
Get all items in collection, filter for get all
var results = coll.Find(FilterDefinition<template>.Empty).ToList();
Get by id, syntax to select by id
var filter = Builders<template>.Filter.Eq(t=>t.templateUniqueId, id);
var result = coll.Find(filter).FirstOrDefault();
Also, for your update method, you can use the following syntax:
var filter = Builders<template>.Filter.Eq(t=>t.templateUniqueId, id);
var update = Builders<template>.Update
.Set(t=>t.outsideClientId, item.outsideClientId)
.Set(t=>t.ClientId, item.ClientId)
.Set(t=>t.isActive, item.isActive)
.Set(t=>t.templateFieldsData, item.templateFieldsData);
var updateResult = coll.UpdateOne(filter, update);
return result.ModifiedCount > 0;
More information about Definitions and Builders:
http://mongodb.github.io/mongo-csharp-driver/2.0/reference/driver/definitions/

Categories