Get field from a sub object in MongoDB - c#

I have the following document in MongoDB and I want to check if the field FileName has a specific value.
Following are my classes:
public class Invoice
{
private InvoiceMetaData _metadata = null;
private List<InvoiceColumns> _invoiceFields = null;
public InvoiceMetaData Metadata
{
get
{
if (_metadata == null) _metadata = new InvoiceMetaData();
return _metadata;
}
set { _metadata = value; }
}
public List<InvoiceColumns> InvoiceFields
{
get
{
if (_invoiceFields == null)
_invoiceFields = new List<InvoiceColumns>();
return _invoiceFields;
}
set { _invoiceFields = value; }
}
}
public class InvoiceMetaData
{
public string FileName { get; set; }
public string FileProcessedOn { get; set; }
public string DirectoryPath { get; set; }
}
I've tried using the following but it's returning false even though documents with this filename exist.
string filename = "01.png";
var collection = myDB.GetCollection<Invoice>(collection_name);
var exists = collection.AsQueryable().Any(avm => avm.Metadata.FileName == filename);
I've also tried this but it's returning nothing i.e. List count is 0.
var query = Query<Invoice>.EQ(u => u.Metadata.FileName, filename).ToBsonDocument();
var exist = collection.Find(query).ToList();
Also tried this and list Count is 0,
var filter1 = Builders<Invoice>.Filter.Eq(u => u.Metadata.FileName, filename, filename);
var result = collection.Find(filter1).ToList();
Can anyone please tell me what am I doing wrong?
Any help will be much appreciated.

I haven`t worked with this for a while. But this can be helpful for you:
how to check if a field exists in a specific document Mongodb using C#?
As i remeber, you can do something like that:
var collection = myDB.GetCollection<BsonDocument>(collection_name); // you use Invoice, try BsonDocument
var documents = collection.Find(new BsonDocument()).ToList(); // this one should get you all documents
var fieldName = "Metadata" // name of field you need data from
foreach (var document in documents)
{
// this one should contain your metadata object from document, note it is BsonDocument
var metaDataObject= document.Contains(fieldName) ? document[fieldName].ToBsonDocument() : null;
var fileName= metaDataObject!= null ? metaDataObject["FileName"] : "No file name."; // should be your file name
// you also should be able to convert to dictionary, under this namespace MongoDB.Bson.BsonDocument
// var metadataDic= metaDataObject.ToDictionary();
// var fileName= metadataDic["FileName"]; // should be your file name
}
Filter examples:
var builder = Builders<BsonDocument>.Filter; // in your example invoice, try BsonDocument
var filter = builder.Eq("FileName", fileName);
var count = collection.Count(filter);

Related

C# Adding multiple elements to a list using xdocument

<?xml version="1.0" encoding="utf-8" ?>
<root>
<fileUploadSpecification>
<DirectoryPath>C:\watchFolder</DirectoryPath>
<Region>us-west-2</Region>
<UploadBucket>configurationtestbucket</UploadBucket>
<FileType>
<type>*.txt</type>
<type>*.OpticomCfg</type>
</FileType>
</fileUploadSpecification>
<fileUploadSpecification>
<DirectoryPath>C:\watchFolder</DirectoryPath>
<Region>us-west-2</Region>
<UploadBucket>loguploadbucket</UploadBucket>
<FileType>
<type>*.Xml</type>
<type>*.Json</type>
</FileType>
</fileUploadSpecification>
</root>
This is the XML file I need to parse, I want to get each instance of fileUploadSpecification so that I can put each set of details into a list, I think some type of for loop would be appropriate, where I loop through and add the first set of upload details and then loop through and add the second. This is what I currently have, but it never gets to the second fileUploadSpecification element, it just returns the same one again.
The idea would be to create a new SettingsData for every set of fileUploadSpecification elements, whether it be two like shown above, or 10.
public interface ISettingsEngine
{
IEnumerable<SettingsData> GetSettings();
}
public class SettingsEngine : ISettingsEngine
{
public IEnumerable<SettingsData> GetSettings()
{
List<SettingsData> dataList = new List<SettingsData>();
try
{
var xDoc = XDocument.Load("File1.xml");
var instancesToParse = xDoc.Root.Elements().Count();
var fileCount = xDoc.Root.Elements("FileType").Count();
for (int x = 0; x < instancesToParse; x++)
{
var newSettingsData = new SettingsData();
newSettingsData.UploadBucket = xDoc.Root.Element("fileUploadSpecification").Element("UploadBucket").Value;
newSettingsData.Region = xDoc.Root.Element("fileUploadSpecification").Element("Region").Value;
newSettingsData.DirectoryPath = xDoc.Root.Element("fileUploadSpecification").Element("DirectoryPath").Value;
var query = xDoc.Root.Descendants("FileType").Elements("type");
foreach (XElement e in query)
{
newSettingsData.FileType.Add(e.Value);
}
dataList.Add(newSettingsData);
}
return dataList;
}
catch(Exception)
{
return dataList;
}
}
}
public class SettingsData
{
public List<string> FileType { get; set; }
public string DirectoryPath { get; set; }
public string Region { get; set; }
public string UploadBucket { get; set; }
public SettingsData()
{
FileType = new List<string>();
}
}
var dataList = (from fus in xDoc.Root.Elements("fileUploadSpecification")
select new SettingsData
{
UploadBucket = fus.Element("UploadBucket").Value,
Region = fus.Element("Region").Value,
DirectoryPath = fus.Element("DirectoryPath").Value,
FileType = fus.Element("FileType")
.Elements("type").Select(f =>f.Value).ToList()
}).ToList();
Each time through the loop, you're looking up the first fileUploadSpecification element all over again. You used the Elements() method already, in a few places. That's the one you want. Always favor foreach over for in C#, when you're looping over a collection. It's quicker (to code, not at runtime) and less error prone.
foreach (var uploadSpec in xDoc.Root.Elements("fileUploadSpecification"))
{
var newSettingsData = new SettingsData();
newSettingsData.UploadBucket = uploadSpec.Element("UploadBucket").Value;
newSettingsData.Region = uploadSpec.Element("Region").Value;
newSettingsData.DirectoryPath = uploadSpec.Element("DirectoryPath").Value;
var types = uploadSpec.Descendants("FileType").Elements("type").Select(e => e.Value);
foreach (var type in types)
{
newSettingsData.FileType.Add(type);
}
// Or if newSettingsData.FileType is List<String>...
//newSettingsData.FileType.AddRange(types);
dataList.Add(newSettingsData);
}
James Curran's answer is functionally the same, but it's better form.

How may I store an array of Xamarin.Forms.Point in SQLite?

How may I store an array of Xamarin.Forms.Point in an SQLite database?
I have the following method:
async void OnSaveButtonClicked(object sender, EventArgs e)
{
var temp = padView.Points;
var tempArray = padView.Points.ToArray();
var databaseItem = new DatabaseItem
{
Name = Name.Text,
Date = Date.Date.ToString(),
//PadViewPoints =
};
//var databaseItem = (DatabaseItem)BindingContext;
await App.Database.SaveItemAsync(databaseItem);
await Navigation.PopAsync();
}
Given there is a signature on the padView, when stepping through the aforementioned method, I see that tempArray has a value of Xamarin.Forms.Point[76].
I'd like to store this array of points in the database so that I may load it later when loading the item.
As Jason said, you could store it as a string, a practical implementation of that would be:
var temp = padView.Points;
var tempArray = padView.Points.ToArray();
var databaseItem = new DatabaseItem
{
Name = Name.Text,
Date = Date.Date.ToString(),
PadViewPointsString = JsonConvert.SerializeObject(tempArray)
};
And in the DatabaseItem class:
public class DatabaseItem
{
// All other properties
string padViewPointsString;
public string PadViewPointsString
{
get => padViewPointsString;
set
{
padViewPointsString = value;
PadViewPoints = JsonConvert.DeserializeObject<Xamarin.Forms.Point[]>(value);
}
}
public Xamarin.Forms.Point[] PadViewPoints { get; set; }
}
This will make the class deserialize the string every time you set it's value (e.g. when you get it from the serialized database).
Obs. This example is using Newtonsoft.Json library
Hope this helps! :)

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/

Fill list from Xml file using Linq to xml

I am trying to fill a list of clients from an xml file using linq, but i always get this null exception: Object reference not set to an instance of an object.. Here is my code, starting with the Client class:
public class Client
{
// Personne Physique
public string IdClient { get; set; }
public string NomClient { get; set; }
public string PrenomClient { get; set; }
public Client(){}
}
The code to fill the list:
var doc = XDocument.Load(pathXml);
List<Client> lc = doc.Descendants("Anzeige")
.FirstOrDefault()
.Descendants("Kunde")
.Select(p => new Client()
{
IdClient = p.Element("KundNr").Value,
NomClient = p.Element("Nachname").Value,
PrenomClient = p.Element("Vorname").Value
}).ToList<Client>();
The xml file looks like this:
<Anzeige>
<Kunde>
<KundNr>111</KundNr>
<Nachname>111</Nachname>
<Vorname>111</Vorname>
</Kunde>
</Anzeige>
Help please! I am pressed by time.
That code will be fine for the sample Xml you have posted.
However you are not handling some scenarios where your code will break. For example:
An Xml document with no <Anzeige> node will cause a null exception
in doc.Descendants("Anzeige").FirstOrDefault().Descendants("Kunde")
as the result from FirstOrDefault() will be null.
An Xml document where one of the <Kunde> nodes doesn't have one of
the value nodes will also cause an exception. For example if there is
no <Vorname> value then this piece of code will throw an exception
p.Element("Vorname").Value
You can tweak a bit your code to handle these scenarios.
Edit: You can use Elements instead of Descendants to force an xml where the Anzeige nodes come directly after the root and Kunde are direct childs of Anzeige. I have also edited my answer to take advantage of the cast operators that you can use directly on an XElement. This way (int?) p.Element("KundNr") returns either an int or null value if the node doesn't exist. Combined with ?? operator its a clean way to read the value. The cast will work with string values and basic value types like int or decimal. (Just for the purpose of demonstrating this I changed IdClient to an int)
You can still have an error if you try to convert to int? and the node value is something that cannot be converted on an int like "ABC". However you had all fields as strings so that shouldn't be a problem for you:
var clients = doc.Descendants("Anzeige").Descendants("Kunde").Select(p => new Client()
{
IdClient = (int?) p.Element("KundNr") ?? -1,
NomClient = (string) p.Element("Nachname") ?? String.Empty,
PrenomClient = (string) p.Element("Vorname") ?? String.Empty
}).ToList();
I have put together a small console application testing a few sample xmls:
static void Main(string[] args)
{
var doc = XDocument.Parse(
#"<Anzeige>
<Kunde>
<KundNr>111</KundNr>
<Nachname>111</Nachname>
<Vorname>111</Vorname>
</Kunde>
<Kunde>
<KundNr>222</KundNr>
<Nachname>222</Nachname>
<Vorname>222</Vorname>
</Kunde>
</Anzeige>");
ExtractClients(doc);
var docWithMissingValues = XDocument.Parse(
#"<Anzeige>
<Kunde>
<KundNr>111</KundNr>
<Vorname>111</Vorname>
</Kunde>
<Kunde>
<KundNr>222</KundNr>
<Nachname>222</Nachname>
</Kunde>
<Kunde>
</Kunde>
</Anzeige>");
ExtractClients(docWithMissingValues);
var docWithoutAnzeigeNode = XDocument.Parse(
#"<AnotherNode>
<Kunde>
<KundNr>111</KundNr>
<Vorname>111</Vorname>
</Kunde>
</AnotherNode>");
ExtractClients(docWithoutAnzeigeNode);
var docWithoutKundeNodes = XDocument.Parse(
#"<Anzeige>
<OtherNode></OtherNode>
</Anzeige>");
ExtractClients(docWithoutKundeNodes);
var emptyDoc = new XDocument();
ExtractClients(emptyDoc);
Console.ReadLine();
}
private static void ExtractClients(XDocument doc)
{
var clients = doc.Descendants("Anzeige").Descendants("Kunde").Select(p => new Client()
{
//You can manually get the value like this:
//IdClient = p.Element("KundNr") != null ? p.Element("KundNr").Value : String.Empty,
//NomClient = p.Element("Nachname") != null ? p.Element("Nachname").Value : String.Empty,
//PrenomClient = p.Element("Vorname") != null ? p.Element("Vorname").Value : String.Empty
//Or directly cast the node value to the type (value types or strings) required like:
IdClient = (int?) p.Element("KundNr") ?? -1,
NomClient = (string) p.Element("Nachname") ?? String.Empty,
PrenomClient = (string) p.Element("Vorname") ?? String.Empty
}).ToList();
Console.WriteLine();
foreach (var client in clients)
{
Console.WriteLine("{0},{1},{2}", client.IdClient, client.NomClient, client.PrenomClient);
}
Console.WriteLine(new string('-',30));
}
public class Client
{
// Personne Physique
public int IdClient { get; set; }
public string NomClient { get; set; }
public string PrenomClient { get; set; }
public Client() { }
}
Hope this helps!

Ignore custom children with Json .Net

i have a json response like this:
{"response_values":[110,{"id":14753,"name":"piter"},{"id":14753,"name":"rabbit"}]}
and i have a simple class
public class Class1
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
and when i trying to convert json to list of the objects with this method:
public T Cast<T>(string json)
{
var result = default(T);
var jsonObject = JObject.Parse(json);
if (jsonObject != null)
{
var responseToken = jsonObject["response"];
result = responseToken.ToObject<T>();
}
return result;
}
like this
...
var jsonString = GetJson();
var items = Cast<List<Class1>>();
...
i have an exceiption, because value "110" is integer. How can i skip this value?
You always have this option if you expect the values to ignore to always be at the beginning:
if (jsonObject != null)
{
var responseToken = parsed["response_values"].SkipWhile(j => j.Type != JTokenType.Object);
if (responseToken.Count() > 0) result = responseToken.ToObject<T>();
}
You may prefer to use Skip(1) instead of SkipWhile if it's always the first value. Alternatively, you can use Where to ignore or select tokens anywhere in the message.
Of course, you can play around with this approach (changing things) depending on exactly what you expect to be returned in success scenarios.

Categories