I have a telerik comobox with filter option enabled. I have added itemtemplate for displaying id and value in the textfield. I want to filter with contains when user type in the dropdown.
Below is the code I tried
<TelerikComboBox Data="#CostCenters"
Filterable="true" FilterOperator="#filterOperator"
Placeholder="Find cost center by typing"
#bind-Value="#SelectedCostCenter1" TextField="CostCenterName" ValueField="CostCenterId">
<ItemTemplate Context="employeeItem">
#( (employeeItem as CostCenter).CostCenterId) #((employeeItem as CostCenter).CostCenterName)
</ItemTemplate>
</TelerikComboBox>
But the above code is only filtering by name
The built-in filtering works on the text (reference) so if you want to filter by multiple fields, you should use the OnRead event and handle the filtering as you need. It's important to note that the contains filter only makes sense for strings, so it is not very likely that you could filter IDs with that since they are usually numbers or guids.
Anyway, here's an example how you can filter the data as required - in this sample - by both ID and string text:
#SelectedValue
<br />
<TelerikComboBox Data="#CurrentOptions"
OnRead=#ReadItems
Filterable="true"
Placeholder="Find a car by typing part of its make"
#bind-Value="#SelectedValue" ValueField="Id" TextField="Make">
</TelerikComboBox>
#code {
public int? SelectedValue { get; set; }
List<Car> AllOptions { get; set; }
List<Car> CurrentOptions { get; set; }
protected async Task ReadItems(ComboBoxReadEventArgs args)
{
if (args.Request.Filters.Count > 0)
{
Telerik.DataSource.FilterDescriptor filter = args.Request.Filters[0] as Telerik.DataSource.FilterDescriptor;
string userInput = filter.Value.ToString();
string method = filter.Operator.ToString(); // you can also pass that along if you need to
CurrentOptions = await GetFilteredData(userInput);
}
else
{
CurrentOptions = await GetFilteredData(string.Empty);
}
}
// in a real case that would be a service method
public async Task<List<Car>> GetFilteredData(string filterText)
{
//generate the big data source that we want to narrow down for the user
//in a real case you would probably have fetched it from a database
if (AllOptions == null)
{
AllOptions = new List<Car>
{
new Car { Id = 1, Make = "Honda" },
new Car { Id = 2, Make = "Opel" },
new Car { Id = 3, Make = "Audi" },
new Car { Id = 4, Make = "Lancia" },
new Car { Id = 5, Make = "BMW" },
new Car { Id = 6, Make = "Mercedes" },
new Car { Id = 7, Make = "Tesla" },
new Car { Id = 8, Make = "Vw" },
new Car { Id = 9, Make = "Alpha Romeo" },
new Car { Id = 10, Make = "Chevrolet" },
new Car { Id = 11, Make = "Ford" },
new Car { Id = 12, Make = "Cadillac" },
new Car { Id = 13, Make = "Dodge" },
new Car { Id = 14, Make = "Jeep" },
new Car { Id = 15, Make = "Chrysler" },
new Car { Id = 16, Make = "Lincoln" }
};
}
if (string.IsNullOrEmpty(filterText))
{
return await Task.FromResult(AllOptions);
}
var filteredData = AllOptions.Where(c => c.Make.ToLowerInvariant().Contains(filterText.ToLowerInvariant()));
// here's an attempt to also filter by ID based on a string, implement this as needed
int id;
if (int.TryParse(filterText, out id))
{
var filteredById = AllOptions.Where(c => c.Id.ToString().Contains(filterText));
filteredData = filteredData.Union(filteredById);
}
return await Task.FromResult(filteredData.ToList());
}
public class Car
{
public int Id { get; set; }
public string Make { get; set; }
}
}
Related
In this contrived example, which closely resembles my real-world problem, I have a data set coming from an external source. Each record from the external source takes the following form:
[Classification] NVARCHAR(32),
[Rank] INT,
[Data] NVARCHAR(1024)
I am looking to build an object where the Rank and Data are patched into a single instance of a response object that contains list properties for the three hard-coded Classification values, ordered by Rank.
I have something that works, but I can't help but think that it could be done better. This is what I have:
public static void Main()
{
IEnumerable<GroupingTestRecord> records = new List<GroupingTestRecord>
{
new GroupingTestRecord { Classification = "A", Rank = 1, Data = "A1" },
new GroupingTestRecord { Classification = "A", Rank = 2, Data = "A2" },
new GroupingTestRecord { Classification = "A", Rank = 3, Data = "A3" },
new GroupingTestRecord { Classification = "B", Rank = 1, Data = "B1" },
new GroupingTestRecord { Classification = "B", Rank = 2, Data = "B2" },
new GroupingTestRecord { Classification = "B", Rank = 3, Data = "B3" },
new GroupingTestRecord { Classification = "C", Rank = 1, Data = "C1" },
new GroupingTestRecord { Classification = "C", Rank = 2, Data = "C2" },
new GroupingTestRecord { Classification = "C", Rank = 3, Data = "C3" },
};
GroupTestResult r = new GroupTestResult
{
A = records.Where(i => i.Classification == "A").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
B = records.Where(i => i.Classification == "B").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
C = records.Where(i => i.Classification == "C").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
};
The source record DTO:
public class GroupingTestRecord
{
public string Classification { get; set; }
public int? Rank { get; set; }
public string Data { get; set; }
}
The destination single class:
public class GroupTestResult
{
public IEnumerable<GroupTestResultItem> A { get; set; }
public IEnumerable<GroupTestResultItem> B { get; set; }
public IEnumerable<GroupTestResultItem> C { get; set; }
}
The distination child class:
public class GroupTestResultItem
{
public int? Rank { get; set; }
public string Data { get; set; }
}
Ouput
{
"A":[
{
"Rank":1,
"Data":"A1"
},
{
"Rank":2,
"Data":"A2"
},
{
"Rank":3,
"Data":"A3"
}
],
"B":[
{
"Rank":1,
"Data":"B1"
},
{
"Rank":2,
"Data":"B2"
},
{
"Rank":3,
"Data":"B3"
}
],
"C":[
{
"Rank":1,
"Data":"C1"
},
{
"Rank":2,
"Data":"C2"
},
{
"Rank":3,
"Data":"C3"
}
]
}
Fiddle
Is there a better way to achieve my goal here?
The same JSON output was achieved using GroupBy first on the Classification and applying ToDictionary on the resulting IGrouping<string, GroupingTestRecord>.Key
var r = records
.GroupBy(_ => _.Classification)
.ToDictionary(
k => k.Key,
v => v.Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank).ToArray()
);
var json = JsonConvert.SerializeObject(r);
Console.WriteLine(json);
which should easily deserialize to the destination single class (for example on a client)
var result = JsonConvert.DeserializeObject<GroupTestResult>(json);
is it possible to get the top level result into a GroupTestResult object?
Build the result from the dictionary
var result = new GroupTestResult {
A = r.ContainsKey("A") ? r["A"] : Enumerable.Empty<GroupTestResultItem>();,
B = r.ContainsKey("B") ? r["B"] : Enumerable.Empty<GroupTestResultItem>();,
C = r.ContainsKey("C") ? r["C"] : Enumerable.Empty<GroupTestResultItem>();,
};
Or this
var result = records.GroupBy(x => x.Classification)
.ToDictionary(x => x.Key, x => x.Select(y => new {y.Rank, y.Data})
.OrderBy(y => y.Rank));
Console.WriteLine(JsonConvert.SerializeObject(result));
Full Demo Here
I would like to be able to attain the same results that I can get by using foreach on a grouping when using the select method and an anonymous method.
public class ExportData
{
public int Id { get; set; }
public string Colour { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Money { get; set; }
}
public class ExportDataDictionary
{
public IDictionary<string, object> ColumnData { get; set; } = new Dictionary<string, object>();
}
Given the two classes above as an example.
I create some data..
var dataCollection = new List<ExportData>
{
new ExportData { Name = "Name1", Age = 1, Colour = "Blue", Id = 1, Money = 10 },
new ExportData { Name = "Name1", Age = 2, Colour = "Red", Id = 2, Money = 20 },
new ExportData { Name = "Name1", Age = 2, Colour = "Green", Id = 3, Money = 30 },
new ExportData { Name = "Name2", Age = 1, Colour = "Yellow", Id = 4, Money = 40 },
new ExportData { Name = "Name3", Age = 2, Colour = "Blue", Id = 5, Money = 50 },
new ExportData { Name = "Name4", Age = 3, Colour = "Blue", Id = 6, Money = 10 }
};
Next I group this data by, for example, two properties as follows..
var dataGrouping = dataCollection.GroupBy(g => new { g.Name, g.Age });
I then create a list of ExportDataDictionaries and foreach through each group in the grouping, creating a new ExportDataDictionary each time and adding both of the keys to the dictionary.
var data = new List<ExportDataDictionary>();
foreach (var grouping in dataGrouping)
{
var datadictionary = new ExportDataDictionary();
datadictionary.ColumnData.Add("NAME", grouping.Key.Name);
datadictionary.ColumnData.Add("AGE", grouping.Key.Age);
data.Add(datadictionary);
}
The result is a collection of 5 ExportDataDictionaries with 2 Columns in each one that contain the pair of keys that correspond to each of the groupings.
My attempt to achieve the same with the Select method is shown below.
var data2 = new List<ExportDataDictionary>();
var mydata = dataGrouping.Select(d =>
{
var datadictionary = new ExportDataDictionary();
datadictionary.ColumnData.Add("NAME", d.Key.Name);
datadictionary.ColumnData.Add("AGE", d.Key.Age);
data2.Add(datadictionary);
return data2;
});
The result is of the type:
mydata = {System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Linq.IGrouping<<>f__AnonymousType0<string, int>, ConsoleApp2.Program.ExportData>, System.Collections.Generic.List<ConsoleApp2.Program.ExportDataDictionary>>}
and it contains 5 items and each item contains 10 dictionaries. The 5 dictionaries that I expect are there with the same values as when using foreach but then there are 2 copies of each. I believe that this must be because it is creating the dictionaries for both of the keys used in the grouping. So, I am wondering how to only do this for one of the keys or just each group in the collection?
The requirement is that mydata should contain the same result as obtained by foreach in data variable
Any help much appreciated :)
Just Add .ToList() at the end of your last statement remove the data2.Add(datadictionary); statement and only return the datadictionary return datadictionary; like this
var mydata = dataGrouping.Select(d =>
{
var datadictionary = new ExportDataDictionary();
datadictionary.ColumnData.Add("NAME", d.Key.Name);
datadictionary.ColumnData.Add("AGE", d.Key.Age);
return datadictionary;
}).ToList();
I have run your code and checked and saw that mydata contains 5 items, and each item contains 2 ColumnData members.
Actually, your Linq query is only executed when you call the .ToList() function
If I have objects, lets call them Group that has list of some other objects I will call it Brand, and this object has a list of objects called Model.
Is there a way to get only list of Models using MongoDb c# driver.
I tried using SelectMany multiple times but with no success. If I put more than one SelectMany I always get an empty list.
Code should be self-explanatory.
At the end is comment that explains what confuses me.
class Group
{
[BsonId(IdGenerator = typeof(GuidGenerator))]
public Guid ID { get; set; }
public string Name { get; set; }
public List<Brand> Brands { get; set; }
}
class Brand
{
public string Name { get; set; }
public List<Model> Models { get; set; }
}
class Model
{
public string Name { get; set; }
public int Produced { get; set; }
}
class Program
{
static void Main(string[] args)
{
MongoClient client = new MongoClient("mongodb://127.0.0.1:32768");
var db = client.GetDatabase("test");
var collection = db.GetCollection<Group>("groups");
var fca = new Group { Name = "FCA" };
var alfaRomeo = new Brand { Name = "Alfra Romeo" };
var jeep = new Brand { Name = "Jeep" };
var ferrari = new Brand { Name = "Ferrari"};
var laFerrari = new Model { Name = "LaFerrari", Produced = 4 };
var wrangler = new Model { Name = "Wrangler", Produced = 3 };
var compass = new Model { Name = "Compass", Produced = 8 };
var giulietta = new Model { Name = "Giulietta", Produced = 7 };
var giulia = new Model { Name = "Giulia", Produced = 8 };
var _4c = new Model { Name = "4C", Produced = 6 };
fca.Brands = new List<Brand> { ferrari, alfaRomeo, jeep };
ferrari.Models = new List<Model> { laFerrari };
jeep.Models = new List<Model> { wrangler, compass };
alfaRomeo.Models = new List<Model> { giulietta, giulia, _4c };
collection.InsertOne(fca);
Console.WriteLine("press enter to continue");
Console.ReadLine();
var models = collection.AsQueryable().SelectMany(g => g.Brands).SelectMany(b => b.Models).ToList();
Console.WriteLine(models.Count); //returns 0 I expected 6
Console.ReadLine();
}
}
Try
var models = collection.AsQueryable()
.SelectMany(g => g.Brands)
.Select(y => y.Models)
.SelectMany(x=> x);
Console.WriteLine(models.Count());
Working output (with extra Select())
aggregate([{
"$unwind": "$Brands"
}, {
"$project": {
"Brands": "$Brands",
"_id": 0
}
}, {
"$project": {
"Models": "$Brands.Models",
"_id": 0
}
}, {
"$unwind": "$Models"
}, {
"$project": {
"Models": "$Models",
"_id": 0
}
}])
OP Output without extra Select()
aggregate([{
"$unwind": "$Brands"
}, {
"$project": {
"Brands": "$Brands",
"_id": 0
}
}, {
"$unwind": "$Models"
}, {
"$project": {
"Models": "$Models",
"_id": 0
}
}])
I am using latest version of mongodb with MongoDB driver for C#. I created a little example, which should explain my problem. My goal ist to make some conditional Count(), conditional First() or an Average() with with filter. None of it works.
What would be the best solution to this problem. Thanks for any hint
class MealDocument
{
public string name { get; set; }
public DateTime time { get; set; }
public Type type { get; set; }
public double calorie { get; set; }
public enum Type { breakfast, launch, dinner }
}
class MealAnalysis
{
public string name { get; set; }
public int numberOfBreakfast { get; set; }
public DateTime firstLaunch { get; set; }
public double averageDinnerCalorie { get; set; }
}
public void Test()
{
var collection = Database.GetCollection<MealDocument>("meal_test");
collection.InsertMany(new MealDocument[] {
new MealDocument { name = "Thomas", type = MealDocument.Type.breakfast, calorie = 100, time = new DateTime(2017,8,1) },
new MealDocument { name = "Thomas", type = MealDocument.Type.breakfast, calorie = 100, time = new DateTime(2017,8,2) },
new MealDocument { name = "Thomas", type = MealDocument.Type.launch, calorie = 800, time = new DateTime(2017,8,3) },
new MealDocument { name = "Thomas", type = MealDocument.Type.dinner, calorie = 2000, time = new DateTime(2017,8,4) },
new MealDocument { name = "Peter", type = MealDocument.Type.breakfast, calorie = 100, time = new DateTime(2017,8,5) },
new MealDocument { name = "Peter", type = MealDocument.Type.launch, calorie = 500, time = new DateTime(2017,8,6) },
new MealDocument { name = "Peter", type = MealDocument.Type.dinner, calorie = 800, time = new DateTime(2017,8,7) },
new MealDocument { name = "Paul", type = MealDocument.Type.breakfast, calorie = 200, time = new DateTime(2017,8,8) },
new MealDocument { name = "Paul", type = MealDocument.Type.launch, calorie = 600, time = new DateTime(2017,8,9) },
new MealDocument { name = "Paul", type = MealDocument.Type.launch, calorie = 700, time = new DateTime(2017,8,10) },
new MealDocument { name = "Paul", type = MealDocument.Type.dinner, calorie = 1200, time = new DateTime(2017,8,11) }
});
var analysis = collection.Aggregate()
.Group(
doc => doc.name,
group => new MealAnalysis
{
name = group.Key,
// !!!! The condition in the Count() gets ignored
numberOfBreakfast = group.Count(m => m.type == MealDocument.Type.breakfast),
// !!!! Exception --> Not supported
averageDinnerCalorie = group.Where(m => m.type == MealDocument.Type.dinner).Average(m => m.calorie),
// !!!! Exception --> Not supported
firstLaunch = group.First(m => m.type == MealDocument.Type.launch).time
}
);
var query = analysis.ToString();
var result = analysis.ToList();
}
I spent a few hours and I can't get driver to work as you need it to. Although I didn't come up with correct solution this is closest I got. I am not a big fan of this solution because it calculates a lot you don't need but it might work good enough if Db is not that large and until you find solution to problem.
var groupRes = collection.Aggregate()
.Group
(
x => new { x.name, x.type },
g => new
{
key = g.Key,
count = g.Count(),
averageCalorie = g.Average(y => y.calorie),
first = g.First().time
}
)
.ToList();
var paulStat = new MealAnalysis()
{
name = "Paul",
averageDinnerCalorie = groupRes.FirstOrDefault(x => (x.key.name == "Paul" && x.key.type == Type.dinner)).averageCalorie,
numberOfBreakfast = groupRes.FirstOrDefault(x => (x.key.name == "Paul" && x.key.type == Type.breakfast)).count,
firstLaunch = groupRes.FirstOrDefault(x => (x.key.name == "Paul" && x.key.type == Type.launch)).first
};
as you can see I calculate stuff you need for each enum and then construct for each person his MealAnalysis. Maybe someone finds solution you need.
Good Luck!
Currently, I work on Csharp Driver LinQ for MongoDb, and I have issues to implement a method that like calling a stored function on MongoDb. Actually,I know that MongoDB doesn't have stored procedure mechanism. And I need someone give suggestion or solution to work around for this. The important thing that is somehow data will be done in mongodb without done in memory. For example, I want to return a list with filter condition which implemented by custom method. This method calculates base on field dependencies .
An example is done in memory.
var list = collection.AsQueryable<Rule>().ToList();
var result = list.Where(x => x.Active && CalcMethod(x.Rule)> 5);
And custom method here.
public static int CalcMethod(Rule rule)
{
if(rule.Active)
// bypass check null
return rule.Weight.Unit * rule.Weight.Value;
else
// return something here
}
The CalcMethod method like a function in SQL Server.
Whether we can do this with MongoDb or other, expect that we can inject a method to calculate data and filter without done in memory.
Every help will be appreciated.
Soner,
I think you can use aggregation for that (or MapReduce for complex things). ie:
async void Main()
{
var client = new MongoClient("mongodb://localhost");
var db = client.GetDatabase("TestSPLike");
var col = db.GetCollection<Rule>("rules");
await client.DropDatabaseAsync("TestSPLike"); // recreate if exists
await InsertSampleData(col); // save some sample data
var data = await col.Find( new BsonDocument() ).ToListAsync();
//data.Dump("All - initial");
/*
db.rules.aggregate(
[
{ $match:
{
"Active":true
}
},
{ $project:
{
Name: 1,
Active: 1,
Weight:1,
Produce: { $multiply: [ "$Weight.Unit", "$Weight.Value" ] }
}
},
{ $match:
{
Produce: {"$gt":5}
}
}
]
)
*/
var aggregate = col.Aggregate()
.Match(new BsonDocument{ {"Active", true} })
.Project( new BsonDocument {
{"Name", 1},
{"Active", 1},
{"Weight",1},
{"Produce",
new BsonDocument{
{ "$multiply", new BsonArray{"$Weight.Unit", "$Weight.Value"} }
}}
} )
.Match( new BsonDocument {
{ "Produce",
new BsonDocument{ {"$gt",5} }
}
})
.Project( new BsonDocument {
{"Name", 1},
{"Active", 1},
{"Weight",1}
} );
var result = await aggregate.ToListAsync();
//result.Dump();
}
private async Task InsertSampleData(IMongoCollection<Rule> col)
{
var data = new List<Rule>() {
new Rule { Name="Rule1", Active = true, Weight = new Weight { Unit=1, Value=10} },
new Rule { Name="Rule2", Active = false, Weight = new Weight { Unit=2, Value=3} },
new Rule { Name="Rule3", Active = true, Weight = new Weight { Unit=1, Value=4} },
new Rule { Name="Rule4", Active = true, Weight = new Weight { Unit=2, Value=2} },
new Rule { Name="Rule5", Active = false, Weight = new Weight { Unit=1, Value=5} },
new Rule { Name="Rule6", Active = true, Weight = new Weight { Unit=2, Value=4} },
};
await col.InsertManyAsync( data,new InsertManyOptions{ IsOrdered=true});
}
public class Weight
{
public int Unit { get; set; }
public int Value { get; set; }
}
public class Rule
{
public ObjectId _id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Weight Weight { get; set; }
}