I am attempting to write a code that finds objects in MongoDB collection with Linq.
Here's my code:
class Program
{
static void Main(string[] args)
{
var client = new MongoClient();
var db = client.GetDatabase("SoundsDB");
var collection = db.GetCollection<Sound>("SoundsCollection");
string myID = "0vvyXSoSHI";
var myObjects = collection
.Find(b => b.objectId == myID);
}
}
public class Sound
{
public string _id { get; set; }
public Result[] results { get; set; }
}
public class Result
{
public Audio_File audio_file { get; set; }
public DateTime createdAt { get; set; }
public string location { get; set; }
public string objectId { get; set; }
public DateTime updatedAt { get; set; }
}
public class Audio_File
{
public string __type { get; set; }
public string name { get; set; }
public string url { get; set; }
}
Here's the JSON in my MongoDB collection:
{
"_id" : ObjectId("56acced71b8ac8702446e8c6"),
"results" : [
{
"audio_file" : {
"__type" : "File",
"name" : "tfss-3c489351-0338-4903-8d94-a0f0c7091ef9-hi.m4a",
"url" : "http://files.parsetfss.com/hithere.m4a"
},
"createdAt" : "2014-12-27T22:59:04.349Z",
"location" : "Home",
"objectId" : "0vvyXSoSHI",
"updatedAt" : "2015-02-26T22:48:02.264Z"
}
]
}
I am trying to make it work but in the following line:
.Find(b => b.objectId == myID)
I get this error:
Cannot convert lambda expression to type 'MongoDB.Driver.FilterDefinition' because it is not a delegate type
Any idea how can I fix it and be able to search through the JSON for objects using their objectId?
Thanks!
I think the problem is that you are searching for a sub-document, not the main doc. Try this:
var myObjects = collection
.Find(b => b.results.Any(r=>r.objectId == myID));
Also - make sure that the objectId value is actually a string in your collection. It seems like it's a string in the object model but an objectId in the db. You may need to (a) change your object model and (b) change that query so that you are asking for r.objectId == ObjectId.Parse(myID) instead of the way I wrote it.
c# MongoDb .Find is Async
If you're using the c# drivers, you probably also need to implement async for this call:
static void Main() {
MainAsync().Wait();
}
static async Task MainAsync() {
var client = new MongoClient();
var db = client.GetDatabase("SoundsDB");
var collection = db.GetCollection<Sound>("SoundsCollection");
string myID = "0vvyXSoSHI";
var myObjects = await collection
.Find(b => b.objectId == myID).ToListAsync();
}
This way, you are using find, and converting the results to a list (so, myObjects will be a List<SoundsCollection> object).
Related
I have a list of objects in below json format. I would like to deserialize using below code. It is throwing unable to convert to object error. I have tried below three options, but didnt help. jsoninput is a IEnumerable<string>converted into json object using ToJson().
Error:
{"Error converting value \"{\"id\":\"11ef2c75-9a6d-4cef-8163-94daad4f8397\",\"name\":\"bracing\",\"lastName\":\"male\",\"profilePictureUrl\":null,\"smallUrl\":null,\"thumbnailUrl\":null,\"country\":null,\"isInvalid\":false,\"userType\":0,\"profilePrivacy\":1,\"chatPrivacy\":1,\"callPrivacy\":0}\" to type 'Api.Models.UserInfo'. Path '[0]', line 1, position 271."}
var requests1 = JsonConvert.DeserializeObject<UsersInfo>(jsoninput);
var requests2 = JsonConvert.DeserializeObject<IEnumerable<UserInfo>>(jsoninput);
var requests3 = JsonConvert.DeserializeObject<List<UserInfo>>(jsoninput);
//Below are my classes,
public class UsersInfo
{
public List<UserInfo> UserInfoList { get; set; }
public UsersInfo()
{
UserInfoList = new List<UserInfo>();
}
}
public class UserInfo
{
public string Id { set; get; }
public string Name { set; get; }
public string LastName { set; get; }
public string ProfilePictureUrl { set; get; }
public string SmallUrl { set; get; }
public string ThumbnailUrl { get; set; }
public string Country { set; get; }
public bool IsInvalid { set; get; }
}
Below is my json object,
["{\"id\":\"11ef2c75-9a6d-4cef-8163-94daad4f8397\",\"name\":\"bracing\",\"lastName\":\"male\",\"profilePictureUrl\":null,\"smallUrl\":null,\"thumbnailUrl\":null,\"country\":null,\"isInvalid\":false}","{\"id\":\"318c0885-2720-472c-ba9e-1d1e120bcf65\",\"name\":\"locomotives\",\"lastName\":\"riddles\",\"profilePictureUrl\":null,\"smallUrl\":null,\"thumbnailUrl\":null,\"country\":null,\"isInvalid\":false}"]
Looping through individual items in json input and if i deserialize it like below, it works fine. But i want to deserialize the list fully. Note: jsoninput was a IEnumerable<string> before i convert in json object.
foreach (var re in jsoninput)
{
var request0 = JsonConvert.DeserializeObject<UserInfo>(re);
}
Please look at this fiddle: https://dotnetfiddle.net/XpjuL4
This is the code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
//Below are my classes,
public class UsersInfo
{
public List<UserInfo> UserInfoList { get; set; }
public UsersInfo()
{
UserInfoList = new List<UserInfo>();
}
}
public class UserInfo
{
public string Id { set; get; }
public string Name { set; get; }
public string LastName { set; get; }
public string ProfilePictureUrl { set; get; }
public string SmallUrl { set; get; }
public string ThumbnailUrl { get; set; }
public string Country { set; get; }
public bool IsInvalid { set; get; }
}
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
Option1();
Option2();
}
public static void Option1(){
string json = #"{""UserInfoList"":[
{""id"":""11ef2c75 - 9a6d - 4cef - 8163 - 94daad4f8397"",""name"":""bracing"",""lastName"":""male"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false},
{ ""id"":""318c0885-2720-472c-ba9e-1d1e120bcf65"",""name"":""locomotives"",""lastName"":""riddles"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false}
]}";
var obj = JsonConvert.DeserializeObject<UsersInfo>(json);
obj.UserInfoList.ForEach(e => Console.WriteLine(e.Id));
}
public static void Option2(){
string json = #"[
{""id"":""11ef2c75 - 9a6d - 4cef - 8163 - 94daad4f8397"",""name"":""bracing"",""lastName"":""male"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false},
{ ""id"":""318c0885-2720-472c-ba9e-1d1e120bcf65"",""name"":""locomotives"",""lastName"":""riddles"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false}
]";
var obj = JsonConvert.DeserializeObject<List<UserInfo>>(json);
obj.ForEach(e => Console.WriteLine(e.Id));
}
}
Both work, and are basically very close to what you are doing. You can either serialize it as a list (based on your json, I think that's the closest to your use case, and that's Option 2).
However, put extra attention to the JSON. I had to re-parse your JSON to make it work (https://jsonformatter.org/json-parser is a nice website to do it). For the sake of explaining the example, in C#, # means raw string, and in raw string, quotes are escaped with double quotes "".
I would expect that the business logic generating this JSON is not correct, if the JSON you pasted is the direct result from it.
EDIT
Given the OP's comment:
Thanks Tu.ma for your thoughts. The other method returns
IEnumerable which is nothing but
Dictionary.Where(x => x.Value == null).Select(x =>
x.Key).ToHashSet(). The values in Dictionary are -> Key
is String, Value is UserInfo object serialized. So, in that case i
should deserialize one by one? If not, i should serialize entire list
in one shot? Am i right? – Raj 12 hours ago
The problem is in the way you are generating the list of UsersInfo. The result from Dictionary<string,string>.Where(x => x.Value == null).Select(x =>
x.Key).ToHashSet() is a bunch of strings, not of objects, so you need to serialize them one by one.
If you are worried about the linearity of the approach, you could consider running through it in parallel. Of course, you need to judge if it fits your application.
var userInfoStrings = Dictionary<string,string>.Where(x => x.Value == null).Select(x => x.Key).ToHashSet();
var UserInfoList = userInfoStrings.AsParallel().Select (u => JsonConvert.DeserializeObject<UsersInfo>(u)).ToList();
[{"service":"xxx",
"processes":
[
{
"name":"tomcat",
"command":{
"start": "/server/tomcat01/bin/tomcat01.sh start",
"stop": "/server/tomcat01/bin/tomcat01.sh stop",
"restart": "/server/tomcat01/bin/tomcat01.sh restart",
}
}
]
}]
how to get start item value with using c# linq?
Given the following concrete classes:
public class Command
{
public string start { get; set; }
public string stop { get; set; }
public string restart { get; set; }
}
public class Process
{
public string name { get; set; }
public Command command { get; set; }
}
public class Services
{
public string service { get; set; }
public List<Process> processes { get; set; }
}
You can deserialize the json and retrieve a list of all starts with the following:
var json = #"[{""service"":""xxx"", ""processes"": [{""name"":""tomcat"", ""command"":{""start"":""/server/tomcat01/bin/tomcat01.shstart"", ""stop"":""/server/tomcat01/bin/tomcat01.shstop"", ""restart"":""/server/tomcat01/bin/tomcat01.shrestart"", } } ] }]";
var deserialized = JsonConvert.DeserializeObject<List<Services>>(json);
var starts = deserialized.Select(x => x.processes.Select(p => p.command?.start));
Using the Json.Net LINQ-to-JSON API you could do this:
string command = JToken.Parse(json)
.SelectMany(jo => jo.SelectToken("processes"))
.Select(jo => (string)jo.SelectToken("command.start"))
.FirstOrDefault();
...which would return /server/tomcat01/bin/tomcat01.sh start given your JSON input.
Fiddle: https://dotnetfiddle.net/Ft3q2C
It seems that you want to deserialize your json string into C# class.
JavaScriptSerializer jss= new JavaScriptSerializer();
CustomClass tempClass = jss.Deserialize<CustomClass>(jsonString);
jsonString is json data which you mentioned in your question. You can then use linq over your class.
My data is having following structure
public enum ParamType
{
Integer=1,
String=2,
Boolean=3,
Double=4
}
public class Gateway
{
public int _id { get; set; }
public string SerialNumber { get; set; }
public List<Device> Devices { get; set; }
}
public class Device
{
public string DeviceName { get; set; }
public List<Parameter> Parameters { get; set; }
}
public class Parameter
{
public string ParamName { get; set; }
public ParamType ParamType { get; set; }
public string Value { get; set; }
}
I filled 10 document objects of Gateway in a MongoDB database.
Now I want to query all those gateways which contains a device having Parameter with ParamName as "Target Temperature" and whose Value > 15.
I created following queries
var parameterQuery = Query.And(Query<Parameter>.EQ(p => p.ParamName, "Target Temperature"), Query<Parameter>.GT(p => int.Parse(p.Value), 15));
var deviceQuery = Query<Device>.ElemMatch(d => d.Parameters, builder => parameterQuery);
var finalQuery = Query<Gateway>.ElemMatch(g => g.Devices, builder => deviceQuery);
But when I run this, it is giving an exception
Unable to determine the serialization information for the expression: (Parameter p) => Int32.Parse(p.Value)
Please suggest where I am wrong.
As the error suggests, you can't use Int32.Parse inside your query. This lambda expression is used to get out the name of the property and it doesn't understand what Int32.Parse is.
If you are querying a string, you need to use a string value for comparison:
var parameterQuery = Query.And(Query<Parameter>.EQ(p => p.ParamName, "Target Temperature"), Query<Parameter>.GT(p => p.Value, "15"));
However, that's probably not what you want to do since you're using GT. To be treated as a number for this comparison you need the value to actually be an int in mongo, so you would need to change the type of your property:
public class Parameter
{
public string ParamName { get; set; }
public ParamType ParamType { get; set; }
public int Value { get; set; }
}
var parameterQuery = Query.And(Query<Parameter>.EQ(p => p.ParamName, "Target Temperature"), Query<Parameter>.GT(p => p.Value, 15));
Summary
I ran into this when I was modifying a list. It appears that Linq First/FirstOrDefault was not handled well by MongoDB for me. I changed to be an array index var update = Builders<Movie>.Update.Set(movie => movie.Movies[0].MovieName, "Star Wars: A New Hope"); Note: This is in Asp.Net 5 using MongoDB.Driver 2.2.0.
Full Example
public static void TypedUpdateExample() {
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("test");
var collection = database.GetCollection<Movie>("samples");
//Create some sample data
var movies = new Movie {
Name = "TJ",
Movies = new List<MovieData>
{
new MovieData {
MovieName = "Star Wars: The force awakens"
}
}
};
collection.InsertOne(movies);
//create a filter to retreive the sample data
var filter = Builders<Movie>.Filter.Eq("_id", movies.Id);
//var update = Builders<Movie>.Update.Set("name", "A Different Name");
//TODO:LP:TSTUDE:Check for empty movies
var update = Builders<Movie>.Update.Set(movie => movie.Movies[0].MovieName, "Star Wars: A New Hope");
collection.UpdateOne(filter, update);
}
public class Movie {
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public List<MovieData> Movies { get; set; }
}
public class MovieData {
[BsonId]
public ObjectId Id { get; set; }
public string MovieName { get; set; }
}
Model structure:
//Main doc model
Public class MainDocumentModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name{ get; set; }
public List<SubDocuementModel> SubDocsList { get; set; }
}
//sub doc model
Public class SubDocumentModel
{
public string Id { get; set; }
[BsonIgnoreIfNull]
public string MainDocId { get; set; }
public string Name{ get; set; }
[BsonIgnoreIfNull]
public List<SubDocuementModel> SubDocsList { get; set; }
.
.
.
}
In DB when I insert MainDocuement, its structure looks like:
{
"_id" : ObjectId("54b9d732bb63bc10ec9bad6f"),
"Name" : "Name-1",
"SubDocsList : [{
"Id" : "54b9e76dbb63bc1890e8761b",
"Name" : "Sub-Name-1"
},....]
}
Now I want to retrieve subdocuments where main doc Id == 54b9d732bb63bc10ec9bad6f:
private void getSubDoc(SubDocumentModel oModel)
{
_MainDoc = _db.GetCollection<MainDocumentModel>("MainDoc");
_query = Query<MainDocumentModel>.Where(e => e.Id == oModel.MainDocId);
//Here I get the exception
//Exception: An error occurred while deserializing the SubDocsList property
of class Data.MainDocumentModel: Element 'Id' does not match any field or
property of class Data.SubDocumentModel.
private MainDocumentModel _mainDocModel = _MainDoc.FindOneAs<MainDocumentModel>(_query);
oModel.SubDocsList =
_mainDocModel .SubDocsList .FindAll(
x => x.Name.ToLower().Contains("NameFilter"));
//Pagination
oModel.SubDocsList =
oModel.SubDocsList .Take(oModel.ItemsPerPage)
.Skip(oModel.ItemsPerPage*(oModel.PageNo - 1))
.ToList();
}
Why I am getting above deserializing exception. What I am doing wrong?
Here I am applying the pagination logic to the retrieved c# list. How I can apply pagination itself in mongo query?
It's very easy to apply pagination for main doc as:
mainDocCursor = MainDoc .Find(_query).SetSortOrder(SortBy.Ascending("Name")).SetLimit(oModel.ItemsPerPage).SetSkip(oModel.ItemsPerPage * (oModel.PageNo - 1));
How can I achieve the same pagination for subdocuments?
I solved the exception of deserializing, with following small changes:
In SubDocumentModel:
Public class SubDocumentModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
.
.
.
}
And when I insert the subdocument, I am saving the Id as object id not as a string (previously I was saving it as a string which was causing the problem):
public void addSubDocument(SubDocumentModeloModel)
{
_MainDoc = _db.GetCollection<MainDocumentModel>("MainDoc");
BsonDocument subDocument = new BsonDocument
{
{"_id", ObjectId.GenerateNewId()},//Previously it was {"Id", ObjectId.GenerateNewId().ToString()}
{"Type", oModel.Name}
};
_query = Query<MainDocumentModel>.Where(e => e.Id == oModel.MainDocId);
_updateBuilder = Update.Push("SubDocsList", subDocument);
_MainDoc.Update(_query, _updateBuilder);
}
However I've still not found a way for pagination of subdocument in mongodb-c#
My application is asp.net. I have to send some values back to server. For this I create a object serialize it and send it to server. At server I try to de-serialize it
Following is my code
[Serializable]
public class PassData
{
public PassData()
{
}
public List<testWh> SelectedId { get; set; }
public string SelectedControlClientId { get; set; }
public string GroupTypeId { get; set; }
public string SectionTypeId { get; set; }
}
[Serializable]
public class testWh
{
public testWh()
{
}
public string Id { get; set; }
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
//this can not serialize the SelectedId and the count remains 0
PassData data = serializer.Deserialize<PassData>(jsonString);
//this serialize in an anonymous object with key value pair
var data2 = serializer.DeserializeObject(textHiddenArguments.Text);
Following is my Json Serialized String
{
"SelectedId":{"0":"ABCD","1":"JKLM"},
"SelectedControlClientId":"YTUTOOO",
"GroupTypeId":3,
"SectionTypeId":"1"
}
quotes escaped string
"{\"SelectedId\":{\"0\":\"ABCD\",\"1\":\"JKLM\"},\"SelectedControlClientId\":\"YTUTOOO\",\"GroupTypeId\":3,\"SectionTypeId\":\"1\"}"
My Problem is Selected Id is array of testWH object. But when I try to desrialize it, the SelectedId property of PassData which is list does not get serialized and count remains zero.
I tried using array instead of List, which gave an exception "no parameter less constructor..."
Could any one explain the what I am doing wrong here ?
The key problem here is that the JSON doesn't match the objects you have constructed. You can see this by writing the data you want and serializing:
var obj = new PassData
{
SelectedId = new List<testWh>
{
new testWh { Id = "ABCD"},
new testWh { Id = "JKLM"}
},
GroupTypeId = "3",
SectionTypeId = "1",
SelectedControlClientId = "YTUTOOO"
};
string jsonString = serializer.Serialize(obj);
which gives JSON like:
{"SelectedId":[{"Id":"ABCD"},{"Id":"JKLM"}],
"SelectedControlClientId":"YTUTOOO","GroupTypeId":"3","SectionTypeId":"1"}
So now you need to decide which you want to change; the JSON or the classes. The following alternative class works fine with your original JSON, for example:
public class PassData
{
public Dictionary<string,string> SelectedId { get; set; }
public string SelectedControlClientId { get; set; }
public string GroupTypeId { get; set; }
public string SectionTypeId { get; set; }
}