Update List<string> in mongoDB - c#

I have a list of strings I want to update in MongoDB using C# driver. How do I do this?
List<string> Images = someList;
var update = Update.Set("Images", Images);
collection.Update(query, update, UpdateFlags.Upsert);
this will give me an error saying that 'Images' is not BsonValue.. How do I convert string list to the bsonvalue? Thanks

It looks like Update.Set is wanting a BsonValue and you can't implicitly convert from List to BsonValue.
You look like you are doing Upserts anyway, could you use Save instead?
One way to solve this issue using Serialization and Save would be:
public class SomeListClass
{
public ObjectId id { get; set; }
public List<string> Images { get; set; }
}
SomeListClass slc = new SomeListClass();
slc.Images = someList;
collection.Save(slc);

That's what I did to solve it: I converted that list to BsonArray:
List<string> Images = someList;
var update = Update.Set("Images", new BsonArray(Images));
collection.Update(query, update, UpdateFlags.Upsert);

If you are using the latest 1.5 version of the C# driver you can also use the new typed Update builder and let it figure out the correct element name and how to serialize the new value.
List<string> images = someList;
var update = Update<SomeListClass>.Set(x => x.Images, images);

Related

Sorting List Array based on an index of array

I want to sort a List Array on the basis of an array item.
I have a List Array of Strings as below:
List<String>[] MyProjects = new List<String>[20];
Through a loop, I have added five strings
(Id, Name, StartDate, EndDate, Status)
to each of the 20 projects from another detailed List source.
for(int i = 0; i<20; i++){
MyProjects[i].Add(DetailedProjectList.Id.ToString());
MyProjects[i].Add(DetailedProjectList.Name);
MyProjects[i].Add(DetailedProjectList.StartDate);
MyProjects[i].Add(DetailedProjectList.EndDate);
MyProjects[i].Add(DetailedProjectList.Status)}
The Status values are
"Slow", "Normal", "Fast", "Suspended" and "" for unknown status.
Based on Status, I want to sort MyProject List Array.
What I have done is that I have created another List as below
List<string> sortProjectsBy = new List<string>(){"Slow", "Normal", "Fast", "", "Suspended"};
I tried as below to sort, however unsuccessful.
MyProjects = MyProjects.OrderBy(x => sortProjectsBy.IndexOf(4));
Can anyone hint in the right direction. Thanks.
I suggest you to create class Project and then add all the fields inside it you need. It's much nicer and scalable in the future. Then create a List or an Array of projects and use the OrderBy() function to sort based on the field you want.
List<Project> projects = new List<>();
// Fill the list...
projects.OrderBy(project => project.Status);
The field Status has to be a primitive type or needs to implement the interface IComparable in order for the sorting to work. I suggest you add an enum for Status with int values.
First consider maybe to use Enum for status and put it in a different file lite (utils or something) - better to work like that.
enum Status {"Slow"=1, "Normal", "Fast", "", "Suspend"}
Now about the filtering you want to achieve do it like this (you need to tell which attribute of x you are referring to. In this case is status)
MyProjects = MyProjects.OrderBy(x => x.status == enum.Suspend);
Read about enums :
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum
Read about lambda expressions :
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions
First of all, storing project details as List is not adivisable. You need to create a Custom Class to represent them.
For example,
public class DetailedProjectList
{
public string Name {get;set;}
public eStatus Status {get;set;}
// rest of properties
}
Then You can use
var result = MyProjects.OrderBy(x=> sortProjectsBy.IndexOf(x.Status));
For example
List<string> sortProjectsBy = new List<string>(){"Slow", "Normal", "Fast", "", "Suspended"};
var MyProjects= new List<DetailedProjectList>{
new DetailedProjectList{Name="abc1", Status="Fast"},
new DetailedProjectList{Name="abc2", Status="Normal"},
new DetailedProjectList{Name="abc3", Status="Slow"},
};
var result = MyProjects.OrderBy(x=> sortProjectsBy.IndexOf(x.Status));
Output
abc3 Slow
abc2 Normal
abc1 Fast
A better approach thought would be to use Enum to represent Status.
public enum eStatus
{
Slow,
Normal,
Fast,
Unknown,
Suspended
}
Then your code can be simplified as
var MyProjects= new List<DetailedProjectList>{
new DetailedProjectList{Name="abc1", Status=eStatus.Fast},
new DetailedProjectList{Name="abc2", Status=eStatus.Normal},
new DetailedProjectList{Name="abc3", Status=eStatus.Slow},
};
var result = MyProjects.OrderBy(x=> x.Status);
Ok so you have a collection of 20 items. Based on them you need to create a list of strings(20 DetailedProjectList items).
What you can do to solve your problem is to SORT YOUR COLLECTION before you create your list of strings. In this way your list of strings will be sorted.
But your code is not optimal at all. So you should concider optimization on many levels.
Lets say you have ProjectDetail class as follow:
private class ProjectDetail
{
public int Id {get;set;}
public string Name {get;set;}
DateTime StartDate {get;set;} = DateTime.Now;
DateTime EndDate {get;set;} = DateTime.Now;
public string Status {get;set;}
public string toString => $"{Id} - {Name} - {StartDate} - {EndDate} - {Status}";
}
Notice that I have added a toString attribute to make things easier, and I also have added default values.
Then your program could be like:
static void Main(string[] args)
{
var projectDetails = MockProjectItems();
Console.WriteLine("Before sortig:");
foreach (var item in projectDetails)
{
Console.WriteLine(item.toString);
}
var myProjects = projectDetails.OrderBy(p => p.Status).Select(p => p.toString);
Console.WriteLine("\n\nAfter sorting:");
foreach (var item in myProjects)
{
Console.WriteLine(item);
}
}
where the helper method is
private static List<ProjectDetail> MockProjectItems()
{
var items = new List<ProjectDetail>(20);
for(int i = 0; i < 20 ; i += 4){
items.Add(new ProjectDetail{Id = i, Name = "RandomName "+i, Status = "Slow"});
items.Add(new ProjectDetail{Id = i+1, Name = "RandomName "+(i+1), Status = "Normal"});
items.Add(new ProjectDetail{Id = i+2, Name = "RandomName "+(i+2), Status = "Fast"});
items.Add(new ProjectDetail{Id = i+3, Name = "RandomName "+(i+3), Status = "Suspended"});
}
return items;
}
Then your program should print the following:

Using Linq ConvertAll to create a backup of a list before altering the list

I'm working on a project where I have a list of objects with a nested list of which I want to make a backup before I update the original list.
I'm close but I'm having trouble finding the correct syntax so I hope you can help me out here.
This is the original class:
public class TeamClass
{
public string Country { get; set; }
public string CountryCode { get; set; }
public List<double> Points { get; set; }
}
List<TeamClass> originalList = new List<TeamClass>();
Just before I update the points on the OriginalList I want to create a deep copy of the OriginalList so I can "undo" the changes.
Currently I'm using the code below:
var backupList = Simulation.ConvertAll(x => new TeamClass
{
Country = x.Country,
CountryCode = x.CountryCode,
Points = new List<double>(x.Points.ConvertAll(p => new double()))
})
.OrderByDescending(o => o.Points.Sum())
.ToList();
Although this code creates a deepcopy of the original list, it replaces all of the points in the list Points by 0 (because of the new double())
However using
Points = x.Points
gives me the correct points but they get updated when the original list gets updated.
I think I have to do something like
Points = new List<double>().AddRange(x.Points)
But I can't find the correct syntax.
Any help is appreciated.
To answer you additional question from comments. It can be done with something like this.
int N = 4; // Number of desired elements with value
var backupList = Simulation.ConvertAll(x => new TeamClass
{
Country = x.Country,
CountryCode = x.CountryCode,
Points = new List<double>(x.Points.Take(N).Concat(x.Points.Skip(N).ToList().ConvertAll(p => new double())));
})
.OrderByDescending(o => o.Points.Sum())
.ToList();
Since double is not nullable datatype, all values after N will be converted to 0.

Comparing two fields of mongo collection using c# driver in mono

Am completely new to Mongodb and C# driver.
Development is being done using Monodevelop on Ubuntu 14.04 and Mongodb's version is 3.2.10 :
Currently my code has a POCO as below:
public class User
{
public String Name { get; set;}
public DateTime LastModifiedAt { get; set;}
public DateTime LastSyncedAt { get; set;}
public User ()
{
}
}
Have been able to create a collection and also to add users.
How do I find users, whose LastModifiedAt timestamp is greater than LastSyncedAt timestamp ? Did some searching, but haven't been able to find the answer.
Any suggestions would be of immense help
Thanks
Actually, it is not very simple. This should be possible with querysuch as :
var users = collection.Find(user => user.LastModifiedAt > user.LastSyncedAt).ToList();
But unfortunetly MongoDriver could not translate this expression.
You could either query all Users and filter on the client side:
var users = collection.Find(Builders<User>.Filter.Empty)
.ToEnumerable()
.Where(user => user.LastModifiedAt > user.LastSyncedAt)
.ToList();
Or send json query, because MongoDb itself is able to do it:
var jsonFliter = "{\"$where\" : \"this.LastModifiedAt>this.LastSyncedAt\"}";
var users = collection.Find(new JsonFilterDefinition<User>(jsonFliter))
.ToList();
And, yes, you need an Id - Property for your model class, i haven't mentioned it first, because i thought you do have one, just not posted in the question.
There is another way to do it. First lets declare collection:
var collection = Database.GetCollection<BsonDocument>("CollectionName");
Now lets add our project:
var pro = new BsonDocument {
{"gt1", new BsonDocument {
{ "$gt", new BsonArray(){ "$LastModifiedAt", "$LastSyncedAt" }
}
} },
{"Name", true },
{"LastModifiedAt", true },
{"LastSyncedAt", true }
};
Now lets add our filter:
var filter = Builders<BsonDocument>.Filter.Eq("gt1", true);
We'll aggregate our query:
var aggregate = collection.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Project(pro)
.Match(filter)
Now our query is ready. We can check our query as follow:
var query=aggregate.ToString();
Lets run our query as follow:
var query=aggregate.ToList();
This with return the required data in list of bson documents.
This solution will work mongo c# driver 3.6 or above. Please comment in case of any confusion. Hopefully i'll able to explain this.

How to display an object property in a drop down list in VB.Net/C# and ASP.NET

I am working with VB.Net and ASP.net, but C# code are welcome too.
I have a list of Clients, the client attributes are:
-ClientID
-FullName
Sample data:
-ClientID:1
-FullName:John Binder
-ClientID:2
-FullName:Leah Brown
And I have a list of Agreements "lstAgreements", their attributes are:
-AgreementID
-ClientID
-Date
Sample data:
AgreementID:5
ClientID:2
Date:12/30/16
AgreementID:7
ClientID:1
Date:12/29/16
"Client" and "Agreement" are entities classes with an existing relation:One client can have many Agreements.
I can retrieve all the agreements with:
Dim lstAgreements As List(Of Agreement) =GetAllAgreements()
And the Agreement Class has a property to get the client object:
lstAgreements(0).ClientObject.FullName
Then I can populate a drop down list with "lstAgreements"
myDropDownList.DataSource = lstAgreements
myDropDownList.DataValueField = "AgreementID"
myDropDownList.DataTextField = "ClientID"
myDropDownList.DataBind()
My question is, how can I display "ClientFullName" instead "ClientID" in the drop Down List?
I am trying this code but it does not work:
myDropDownList.DataSource = lstAgreements
myDropDownList.DataValueField = "AgreementID"
myDropDownList.DataTextField = "ClientObject.FullName"
myDropDownList.DataBind()
I noticed that using "ClientObject.FullName" in a DataList control it works fine, but in Drop Down List it doesn't.
Any suggestion?
Thanks in advance
You could loop through lst Agreements rather than using a datasource/databind and create the list items yourself?
something like
foreach (Agreement a in lstAgreements)
{
myDropDownList.Items.Add(new ListItem(a.ClientObject.FullName, a.AgreementId))
}
or use linq to set your full name from the client list if you have no full name method to return full name in agreement.
Why didn't you just use it like this :
myDropDownList.DataTextField = "FullName";
Update:
Obviously , Agreement does not contain a property with the name 'FullName'. it has -AgreementID -ClientID -Date properties.
I would recommend using Linq to retrive the data you need from multiple lists (C#):
public class CustomList
{
public string FullName { get; set; }
public int AgreementID{ get; set; }
}
var Custom = from i in listofClients
from j in listofAgreements
Where i.ClientID == j.ClientID
select new CustomList{
FullName = i.FullName ,
AgreementID = j.AgreementID
}.ToList();
Now you can use Custom to bind it to your DropDownList:
myDropDownList.DataSource = Custom ;
myDropDownList.DataValueField = "AgreementID";
myDropDownList.DataTextField = "FullName";
myDropDownList.DataBind();
Depending on your controls (though I think this is a default behavior), you can also do this by overriding the ToString() method for your object. Normally, this would give a string representation of its type, but if you change the return value to be the desired property, that will be the value of ToString(). If you then bind to something like IEnumerable lstAgreements, you don't even have to property search. The value for the dropdownlist item will be the object, and the text will be its ToString() output.
Pseudo:
prop int AgreementID
prop int ClientID
prop DateTime Date
prop ClientObject CO;
public IEnumerable<Agreement> GetAllAgreements()...
public override string ToString()
{
return this.CO.FullName;
}
-----------
mydropdown.DataSource = GetAllAgreements(); Databind();

Get a single element of CSV file

I'm trying to add some csv elements to a list of Alimento, where Alimento is declared as:
namespace ContaCarboidrati
{
class Alimento
{
public virtual string Codice { get; set; }
public virtual string Descrizione { get; set; }
public virtual int Carboidrati { get; set; }
}
}
My csv looks something like this:
"C00, Pasta, 75".
Here's the method that should create the list from the csv:
private static List<Alimento> CreaListaAlimentiDaCsv()
{
List<Alimento> listaCsv = new List<Alimento>();
StreamReader sr = new StreamReader(#"C:\Users\Alex\Documents\RecordAlimenti.csv");
string abc = sr.ReadLine();
//listaCsv = abc.Split(",");
}
abc is "C00, Pasta, 75". I want to get a single element to add it to the list, or add all the 3 elements to the list, i thought that a single element is easier to made.
Sorry for my bad English
Thanks in advance
Alex
You are on the right track, but you cannot just create an Alimento of three strings, which is what you will get if you do abc.Split(","). You need to create a new Alimento object for each item (line) in the csv file and initialize each object correctly. Something like this:
var item = abc.Split(',');
listaCsv.Add(new Alimento() { Codice = item[0], Descrizione = item[1],
Carboidrati = int.Parse(item[2])};
Also, your csv seems to include spaces after the commas which you might want to get rid of. You could use string.Trim() to get rid of leading/trailing spaces. You also have to make sure the third item is actually an integer and take action if that is not the case (i.e. add some error handling).
As a side note, implementing a csv reader is not as trivial as one may think, but there are several free C# implementations out there. If you need something a bit more advanced than just reading a simple (and strictly one-line-per-item) csv, try one of these:
http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader
http://www.filehelpers.com/
You can parse file with LINQ
var listaCsv = (from line in File.ReadAllLines("RecordAlimenti.csv")
let items = line.Split(',')
select new Alimento {
Codice = items[0],
Descrizione = items[1],
Carboidrati = Int32.Parse(items[2])
}).ToList();
You can parse it pretty easy assuming your data isn't bad.
private IEnumerable<Alimento> CreaListaAlimentiDaCsv(string fileName)
{
return File.Readlines(fileName) //#"C:\Users\Alex\Documents\RecordAlimenti.csv"
.Select(line => line.Split(',').Trim())
.Select(
values =>
new Alimento
{
Codice = value[0],
Descrizione = values[0],
Carboidrati = Convert.ToInt32(values[3])
});
}
You can also use Linq on the method such as
//Takes one line without iterating the entire file
CreaListaAlimentiDaCsv(#"C:\Users\Alex\Documents\RecordAlimenti.csv").Take(1);
//Skips the first line and takes the second line reading two lines total
CreaListaAlimentiDaCsv(#"C:\Users\Alex\Documents\RecordAlimenti.csv").Skip(1).Take(1);

Categories

Resources