InitializeComponent();
var connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var database = client.GetDatabase("test");
var collection = database.GetCollection<Rootobject>("container");
var res = collection.Aggregate().Group(x => x._event.NodeId, x => new { Name = x.Key, WTs = x.Select(r => r._event.WT).ToList() }).ToList();
cartesianChart1.Series = new SeriesCollection
{
new LineSeries
{
Title = res["event"]["NodeId"] ,
Values = new ChartValues<double> {4.0, 3.9, 3.9, 3.85, 3.8, 3.8, 3.75}
},
}
The code above , i am trying to use my mongodb for my graphs input . But first i need to be able to query from MongoDB , i have come up with a var record but the 'NodeId' still has errors. it has Event[] does not contain a definition for 'NodeID' and no extension method 'NodeId' accepting a first argument of type Event[]. Below are my public classes
public class Rootobject
{
public string _id { get; set; }
public Header[] header { get; set; }
public Event[] _event { get; set; }
}
The problem is in this part of your code:
.Group(x => x._event.NodeId
Your _event property is of type Event[], which is an array of Event. You can get one item of your array and access the NodeId property (e.g. the from the first element with _event[0].NodeId, which will work if this element exists), but not from the array itself.
I can not tell you with what you should replace this part with, because that depends entirely on what you want to achieve.
Related
I have below class structure and a list of CollectionInstance
public class CollectionInstance
{
public string Name { get; set; }
public List<CollectionProperty> CollectionProperties { get; set; }
}
public class CollectionProperty
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
Here is list of CollectionInstance. Currently it has only two data types double and string, but I have more data types
var lstCollectionInstances = new List<CollectionInstance>
{
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 10, DataType = "Double"}
}
},
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H1", DataType = "String"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 20, DataType = "Double"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H2", DataType = "String"}
}
},
};
Now my goal to fetch all the different data type and filter list of CollectionInstance based on the data type. May be a dictionary or could be other collection as well, where I should store data type as key and filtered CollectionInstance as a value.
I tried below, but what could be the best way?
var dictionary = new Dictionary<string, List<CollectionInstance>>();
var dataTypesGroups = lstCollectionInstances
.SelectMany(x => x.CollectionProperties).GroupBy(x => x.DataType);
foreach (var dataType in dataTypesGroups)
{
dictionary.Add(dataType.Key, GetFilterData(lstCollectionInstances, dataType.Key));
}
private static List<CollectionInstance> GetFilterData(IEnumerable<CollectionInstance> lst, string dataType)
{
return lst.Where(x => x.CollectionProperties.Any(y => y.DataType == dataType)).ToList();
}
You could keep reference to parent CollectionInstance when grouping and reuse that when selecting results:
lstCollectionInstances
.SelectMany(x => x.CollectionProperties, (i, c) => new {CollectionInstance = i, CollectionProperty = c})
.GroupBy(x => x.CollectionProperty.DataType)
.ToDictionary(c => c.Key, c => c.Select(d => d.CollectionInstance) )
UPD
here we leverage this overload of .SelectMany(). So Instead of List<CollectionProperty> you end up having List<Tuple<CollectionInstance,CollectionProperty>> (well, i opted for anonymous type, but this does not matter much). You basically enhance each child object with reference to its parent. And since all these are just references - you don't trade a lot of memory for having it.
And when you group it - you get an option to not select the CollectionProperty, but rather the parent object directly.
I hope this makes sense
Build a dictionary that for each data type stores a list of instances with a property of the key data type.
var result = instances
.SelectMany(x => x.Properties)
.Select(x => x.DataType)
.Distict()
.ToDictionary(x => x, x => GetInstancesWithPropertyOfType(x, instances));
Given the following is defined:
public class Instance
{
public string Name { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
List<Instance> GetInstancesWithPropertyOfType(string dataType, IEnumerable<Instance> instances) =>
instances.Where(x => x.Properties.Any(y => y.DataType == dataType)).ToList();
Personally think that using LINQ on this just makes it more unreadable and harder to understand. This is basically a two loop operation; for each x in instances/foreach y in x.properties/add x to dictionary indexed by y.z and would be most easily understood by keeping it as such. This minimizes the amount of work done by the framework too; here we create no unnecessary extra objects, lists, groupings etc in the quest to enumerate a 2-deep object hierarchy and create a dictionary, and even a coder who never saw LINQ can understand it:
var dictionary = new Dictionary<string, List<CollectionInstance>>();
foreach (var ci in lstCollectionInstances){
foreach(var cp in ci.CollectionProperties){
if(!dictionary.ContainsKey(cp.DataType))
dictionary[cp.Key] = new List<CollectionInstance>();
dictionary[cp.Key].Add(ci);
}
}
LINQ is a hammer; not every problem is a nail
I have the following code which returns results from a database table comprising of an Id field and a Name field, and transfers it to a list of SelectListItems (this populates a dropdown box in my view.)
var locationTypes = await APIHelper.GetAsync<List<LocationType>>(url);
var items = new List<SelectListItem>();
items.AddRange(locationTypes.Select(locationType =>
{
var item = new SelectListItem();
item.Value = locationType.LocationTypeId.ToString();
item.Text = locationType.Name;
return item;
}));
I am repeating this a lot throughout my application, substituting LocationType for various other things. The item.Value always gets the Id property of the data returned (the Id field is always in the format of {TableName}+"Id"), and the item.Text always gets ".Name" property.
How can I make this generic? I am trying to achieve something like this, although it is syntactically incorrect and may be the incorrect approach:
var myGenericObjects = await APIHelper.GetAsync<List<T>>(url)
var items = new List<SelectListItem>();
items.AddRange(myGenericObjects .Select(myGenericObjects =>
{
var item = new SelectListItem();
item.Value = myGenericObject.Field[0].ToString();
item.Text = myGenericObject.Name;
return item;
}));
You can create a custom extension for a generic list object, then, using reflection retrieve the values that you are wanting to map to the SelectListItem.Text and Name fields. Note I am using "nameof" in order to prevent any confusion or magic string representations of the properties to which I am trying to map.
I did define a default value of "Name" to the namePropertyName parameter. Per your description it sounded like, by convention, most of your DTOs have the property "Name" in them. If that's not the case simply remove the default value that is defined.
There are additional checks that could be made to this extension to prevent NullReference and ArgumentExceptions as well, but for simplicity of the example were left out. Example: Ensuring a value is provided in the idPropertyName and namePropertyName parameters and ensuring those property names exist on the provided generic object prior to conversion.
public static class ListExtensions
{
public static List<SelectListItem> ToSelectList<T>(this List<T> list, string idPropertyName, string namePropertyName = "Name")
where T : class, new()
{
List<SelectListItem> selectListItems = new List<SelectListItem>();
list.ForEach(item =>
{
selectListItems.Add(new SelectListItem
{
Text = item.GetType().GetProperty(namePropertyName).GetValue(item).ToString(),
Value = item.GetType().GetProperty(idPropertyName).GetValue(item).ToString()
});
});
return selectListItems;
}
}
Example Use:
var testList = new List<TestDto>
{
new TestDto { Name = "Test0", TestId = 0 },
new TestDto { Name = "Test1", TestId = 1 },
new TestDto { Name = "Test2", TestId = 2 },
new TestDto { Name = "Test3", TestId = 3 },
new TestDto { Name = "Test4", TestId = 4 },
};
var selectList = testList.ToSelectList(nameof(TestDto.TestId), nameof(TestDto.Name));
Here is the TestDto class for reference:
public class TestDto
{
public int TestId { get; set; }
public string Name { get; set; }
}
Some Prep Work
If you can change the table column names, then use a convention. For example, always name the "Value" column "X", and the "Text" column "Y" (give them better names). Then make all the classes for those tables implement an interface similar to this:
public interface ICustomLookup
{
string X { get; set; }
string Y { get; set; }
}
public class SomeClass : ICustomLookup
{
public string X { get; set; }
public string Y { get; set; }
}
Then an extension method like so:
public static class EnumerableExtension
{
public static SelectList ToSelectList(this IEnumerable<ICustomLookup> items)
{
return new SelectList(items.Select(thisItem => new SelectListItem
{
Text = thisItem.X,
Value = thisItem.Y
}));
}
}
Usage
var items = new List<SomeClass>
{
new SomeClass { X = "XOne", Y = "YOne" },
new SomeClass { X = "XTwo", Y = "YTwo" }
};
SelectList selectList = items.ToSelectList();
I have generic object and I need to get item {code, description} from it using LINQ Query
Class
[Serializable]
[XmlRoot("Genders")]
public class Gender
{
[XmlElement("Genders")]
public List<GenderListWrap> GenderListWrap = new List<GenderListWrap>();
}
public class GenderListWrap
{
[XmlAttribute("list")]
public string ListTag { get; set; }
[XmlElement("Item")]
public List<Item> GenderList = new List<Item>();
}
public class Item
{
[XmlElement("CODE")]
public string Code { get; set; }
[XmlElement("DESCRIPTION")]
public string Description { get; set; }
}
here in following screen shot I see my data in GenderObject
* EDIT *
Based on your edits, it appears your issue is related to the datatype you are receiving from your "ObjectToXML" method - this returns something of type Object by the looks of your code, not something of the type you have specified in your question. You could try to cast to the expected type eg. Gender GenderObject = (Gender)SystemCore.XMLPrasing.ObjectToXML(...), which may fail (if the returned type isn't actually Gender). Or you could use the .NET built in XML deserialization, which will be able to return objects of the correct type.
Despite the rather odd data structure you have (whay such effort for a list of genders?), if you want a List<Item>, the following will project just the items into a list:
genders.GenderListWrap.SelectMany(l => l.GenderList).ToList();
(This is assuming you have an instance called genders, constructed something like:
var genders = new Gender
{
GenderListWrap = new List<GenderListWrap>
{
new GenderListWrap
{
GenderList = new List<Item>
{
new Item { Code = "F", Description = "Female" },
new Item { Code = "M", Description = "Male" },
}
},
new GenderListWrap
{
GenderList = new List<Item>
{
new Item { Code = "N", Description = "Neutral" },
}
}
}
};
var genderList = genders.GenderListWrap.SelectMany(l => l.GenderList).ToList();
If, however, you want to get a specific item, you can just use the standard index accessors mentioned in comments, eg. genders.GenderListWrap[0].GenderList[0]
This should work for you, if you cast the object you have to Gender and then do a select on it:
var genderList = ((Gender)GenderObject).GenderListWrap.SelectMany(x => x.GenderList);
This will return a collection of Items on which you will have the {code, description} you are looking for.
I have a list of Ids
List<string> Ids;
and I would like to retrieve all the documents matching these Ids.
There are solutions on the web:
var ids = new int[] {1, 2, 3, 4, 5};
var query = Query.In("name", BsonArray.Create(ids));
var items = collection.Find(query);
but they're all with the old C# driver and with the (not so new anymore) 2.2.4 driver the API has changed and I can't find how to build this query.
please see snippet below (made using LINQPad)
void Main()
{
// To directly connect to a single MongoDB server
// or use a connection string
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("test");
var collectionEmpInfo = database.GetCollection<Employee>("Employee");
Employee EmpInfo = new Employee
{
EmpID = "103",
EmpName = "John",
CreatedAt = DateTime.Now,
EmpMobile = new List<Mobile>
{
new Mobile{ MobNumber = "55566610", IsPreferred = true, MobID = ObjectId.GenerateNewId() },
new Mobile{ MobNumber = "55566611", IsPreferred = false, MobID = ObjectId.GenerateNewId() },
}
};
//collectionEmpInfo.InsertOne(EmpInfo);
var filterDef = new FilterDefinitionBuilder<Employee>();
var filter = filterDef.In(x=>x.EmpID , new[]{"101","102"});
filter.Dump();
var empList = collectionEmpInfo.Find(filter).ToList();
empList.Dump();
}
public class Employee
{
public ObjectId Id { get; set; }
public string EmpID { get; set; }
public string EmpName { get; set; }
public List<Mobile> EmpMobile { get; set; }
public DateTime CreatedAt { get; set; }
}
public class Mobile
{
public ObjectId MobID { get; set; }
public string MobNumber { get; set; }
public bool IsPreferred { get; set; }
}
and results screenshot
This looks to be the current equivalent of what you were attempting:
var ids = new int[] {1, 2, 3, 4, 5};
var filter = Builders<BsonDocument>.Filter.In("name", ids);
var items = collection.Find(filter);
I struggled with this recently.
Using the new driver (v2.14.X) this can be achieved with the $in operator. This Mongo link describes the $in functionality reasonably well.
You can either apply the LINQ format:
var ids = new List<string> {"id1", "id2", "etc"};
someMongoCollection.Find(o => o.Any(i => ids.Contains(o.Id));
The LINQ format works well except for cases where you're trying to chain queries together (i.e. if you are querying with optional data, so int[] ages, string[] ids, etc.). In that case you effectively want to build up the query as necessary.
For that application I use the native mongo format (also described in the Mongo link above).
{ Id: { $in: ["id1", "id2", "etc"] } }
This is passed as a string to create a BsonDocument which is then used as the filter on the .Find() operation. You can add more filters as necessary. More information on passing in data to create BsonDocument objects can be found at this link.
See example code snip below:
// 1 - init base filter (this fetches everything)
var filter = new BsonDocument();
// 2 - init id data
var ids = new List<string> {"id1", "id2", "etc"};
var idsString = ids.ConvertAll(i => $"\"{i}\"");
// 3 - create the mongo formatted query and generate the BsonDocument
var bsonToParse = $"{{ \"Id\": {{ $in: [{string.Join(",", idsString )}] }} }}";
var idsFilterBson = BsonDocument.Parse(bsonToParse);
// 4 - add the BsonDocument to the already existing filter
filter.Add(idsFilterBson);
// perform steps 2-4 for any other optional query data
// run the final find operation
var dataFromDb = await someMongoCollection.Find(filter).Limit(limit).ToListAsync();
with latest c# mongo driver you also can leverage LINQ support:
var results = this.mongoClient.GetCollection<Employee>()
.AsQueryable()
.Where(x => idList.Contains(x.Id))
.ToList();
Can I restructure the following into a more compact linq query, ideally without the introduction of a helper function?
var revPerUnitChanges =
from row in this.DataTable.GetChanges(DataRowState.Modified).AsEnumerable()
let field = "Rev/Unit"
select new {
Field = field,
From = row.Field<decimal>(field, DataRowVersion.Original),
To = row.Field<decimal>(field, DataRowVersion.Current),
ItemIds = row.Field<string>("ItemIds"),};
var costPerUnitChanges =
from row in this.DataTable.GetChanges(DataRowState.Modified).AsEnumerable()
let field = "Cost/Unit"
select new {
Field = field,
From = row.Field<decimal>(field, DataRowVersion.Original),
To = row.Field<decimal>(field, DataRowVersion.Current),
ItemIds = row.Field<string>("ItemIds"), };
var numUnitsChanges =
from row in this.DataTable.GetChanges(DataRowState.Modified).AsEnumerable()
let field = "Units"
select new {
Field = field,
From = row.Field<decimal>(field, DataRowVersion.Original),
To = row.Field<decimal>(field, DataRowVersion.Current),
ItemIds = row.Field<string>("ItemIds"), };
var changes =
revPerUnitChanges
.Concat(costPerUnitChanges
.Concat(numUnitsChanges))
.Where(c => c.From != c.To);
Start out by creating a helper class to hold onto the data. (Your code doesn't have any problems using anonymous types, but if you want to refactor sections into methods it'll be much easier with a named class.)
public class MyClass //TODO give better name
{
public MyClass(DataRow row, string field) //You could have a public static generate method if it doesn't make sense for this to be a constructor.
{
Field = field;
From = row.Field<decimal>(field, DataRowVersion.Original);
To = row.Field<decimal>(field, DataRowVersion.Current);
ItemIds = row.Field<string>("ItemIds");
}
public string Field { get; set; }
public decimal From { get; set; }
public decimal To { get; set; }
public string ItemIds { get; set; }
}
Now that we have that out of the way the query is fairly straightforward.
var changes = dataTable.GetChanges(DataRowState.Modified)
.AsEnumerable()
.Select(row => new[]{ //create 3 new items for each row
new MyClass(row, "Rev/Unit"),
new MyClass(row, "Cost/Unit"),
new MyClass(row, "Units"),
})
.SelectMany(item => item) //flatten the inner array
.Where(item => item.From != item.To);