I am trying to obtain a child element of a parent element. I have a basic database as so:
public class list
{
public int id { get; set; }
public List<Users> UsersList{ get; set; }
public class Users
{
[Key]
public int Users_id{ get; set; }
public string UserId { get; set; }
}
}
If I was wanting to obtain all of the of the elements in Users that had the specific UserId how would I do that? I am trying to refrain from using a nested for loop and iterating through all my entries of List and Users in the database. I was looking up the LookUp(), but am a bit confused on how to apply it in this case. Any help would be great!
Since you're a bit confused, I'll provide more detail than my original answer. Let's take your code and create a very basic and crude sample program:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace SOSample
{
public class list
{
public int id { get; set; }
public List<Users> UsersList { get; set; }
public class Users
{
[Key]
public int Users_id { get; set; }
public string UserId { get; set; }
}
}
class Program
{
static void Main(string[] args)
{
// Instantiate and initialize with sample data.
var sampleList = new list()
{
id = 12345,
UsersList = new List<list.Users>()
{
new list.Users() { Users_id = 1, UserId = "0042" },
new list.Users() { Users_id = 2, UserId = "0019" },
new list.Users() { Users_id = 3, UserId = "0036" },
new list.Users() { Users_id = 4, UserId = "0214" },
new list.Users() { Users_id = 5, UserId = "0042" },
new list.Users() { Users_id = 6, UserId = "0042" },
new list.Users() { Users_id = 7, UserId = "0019" }
}
};
// Linq search.
var someId = "0042";
var linqQuery = sampleList.UsersList.Where(user => user.UserId == someId);
Console.WriteLine("Linq query results:");
foreach (var r in linqQuery)
{
Console.WriteLine($"Users_id: {r.Users_id}, UserId: {r.UserId}");
}
// Lookup search (using same someId as for Linq).
var lookup = sampleList.UsersList.ToLookup(user => user.UserId);
var lookupQuery = lookup[someId];
Console.WriteLine("\nLookup query results:");
foreach (var r in lookupQuery)
{
Console.WriteLine($"Users_id: {r.Users_id}, UserId: {r.UserId}");
}
}
}
}
Output:
Linq query results:
Users_id: 1, UserId: 0042
Users_id: 5, UserId: 0042
Users_id: 6, UserId: 0042
Lookup query results:
Users_id: 1, UserId: 0042
Users_id: 5, UserId: 0042
Users_id: 6, UserId: 0042
Hope that clarifies things. The major issue I see with your question and comments is that it's possible that you're mistaking nested classes for properties. When you instantiate an outer class, the inner class does not get instantiated and it's not some sort of property of an outer class.
Old answer (provides individual details):
I like using Linq. So, assuming sampleList is of type list:
var query = sampleList.UsersList.Where(user => user.UserId == someId);
That's going to give you IEnumerable<list.Users>. You can always use ToList(), ToArray(), ToDictionary() to get the desired collection type:
var results = sampleList.UsersList.Where(user => user.UserId == someId).ToArray();
As far as Lookup, I've seen a few ways it being used, but the most familiar way for me is this:
var lookup = sampleList.UsersList.ToLookup(user => user.UserId);
var query = lookup[someId];
Once again, that'll give you IEnumerable<list.Users>. Alternatively, you can get the collection type of your choice from that query:
var results = lookup[someId].ToArray();
Basically, you're specifying what the key will represent in that lookup (it's the UserId in this case) and then when the time comes, you search by a key.
Related
Hi and thanks in advance everyone!
I have a collection of the following objects:
public class ItemsModel
{
public List<int> IdCollection { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
}
List<ItemsModel> col = ...;
I want to optimally store this with LiteDb and be able to modify the records.
Each ItemsModel has a unique Name+Weight set.
In the entire col, the elements of the IdCollection are also unique.
Body example:
List<ItemsModel>:
[{
IdCollection: [1,3,5,6,...],
Name: "first name",
Weight: 10
},
{
IdCollection: [2,4,...],
Name: "second name",
Weight: 5
}]
I want to index by Id
I want to expand into two tables for easy storage in LiteDb:
[{
_id: 1,
NameAndWeight: {&ref: "names"}
},
{
_id: 2,
NameAndWeight: {&ref: "names"}
},
{
_id: 3,
NameAndWeight: {&ref: "names"}
},
...
]
[{
Name: "first name",
Weight: 10
},
{
Name: "second name",
Weight: 5
}]
For this I have to make new storage classes:
public class ItemsModel
{
[BsonId]
public int Id { get; set; }
[BsonRef("names")]
public NamesModel NameAndWeight { get; set; }
}
public class NamesModel
{
[BsonId(true)]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
}
But next step I'm having trouble...
Tell me, can I somehow save data using Insert array and Include in one operation?
Or should I use foreach to first write the NamesModel in "names" DB, get the generated _id, then write the ItemsModel with a link to the NamesModel already written to the database?
using (var db = new LiteDatabase(_strConnection))
{
var itemsDb = db.GetCollection<ItemsModel>("items");
var namesDb = db.GetCollection<NamesModel>("names");
itemsDb.EnsureIndex(x => x.Id, true);
foreach (var group in col)
{
var name = new NamesModel(group.Name, group.Weight);
namesDb.Insert(name);
var itemDb = group.IdCollection.Select(el => new ItemsModel(el, name));
var h = itemsDb.Insert(itemDb);
}
}
it is too long(
Now I did like this:
using (var db = new LiteDatabase(_strConnection))
{
var itemsDb = db.GetCollection<ItemsModel>("items");
var namesDb = db.GetCollection<NamesModel>("names");
itemsDb.EnsureIndex(x => x.Id, true);
namesDb.EnsureIndex(x => x.Name);
var temp = col.Select(el => (el.IdCollection, new NamesModel(el.Name, el.Weight))).ToList();
namesDb.Insert(temp.Select(el => el.Item2));
var temp2 = temp.SelectMany(gr => gr.IdCollection.Select(el => new ItemsModel(el, gr.Item2)));
eventsIdDB.Insert(temp2);
}
Performed basic operations in linq to reduce the number of hits in liteDb
I have an Account object like:
public class Account
{
public int Id { get; set; }
public List<Elegibilities> Elegibilities { get; set; }
}
public class Elegibilities
{
public int Id { get; set; }
public string Label { get; set; }
public bool Value { get; set;}
}
My repository returns a List of Account. List<Account>
I want to group by and count by items in Elegibilities and the output would be like:
{
Total: 30,
[
{
Id : 1,
Label: "Elegibility1",
Count: 10
},
{
Id : 2,
Label: "Elegibility2",
Count: 20
}
]
}
I'm having issues because it's a list inside another list.
Is it possible to solve in a single LINQ query?
Look's like you don't want any data related to the Accounts,
This gives you a simple flat list to work with.
var flatList = accounts.SelectMany(a => a.Elegibilities);
This could be another example if you needed to include accounts related data to the output.
var group = from a in accounts
from el in a.Elegibilities
group el by el.Id into elGroup
select new
{
Id = elGroup.Key,
Count = elGroup.Count(),
Label = elGroup.First().Label
};
In the end you can access your group totals.
var result = new { Totals = group.Count() , Elegibilities = group.ToList() };
I'm using EF 6 Code-First and I want my costumers to introduce new records in multiple languages. To store this I save all the texts in a Dictionary entity with a custom key and reference it from my other entity through this key.
This is a basic sample of my entities:
Dictionary
ID Key IsoCode Value
1 firstEntry en-US My new entry
2 secondEntry en-US My second entry
3 firstEntry es-ES Mi nueva entrada
Entries
ID Name CreationDate
1 firstEntry 2020-11-04
2 secondEntry 2020-11-07
What I want to achieve is to query the Entries entity with an ISO Code, and get a new Entity where the Name field is replaced with Value field from the Dictionary entity.
This is what I have right now:
public List<Entry> GetEntries(string isoCode)
{
var query = (from e in dbContext.Entry
join d in dbContext.Dictionary on e.Name equals d.Key
where d.IsoCode == isoCode
select new
{
entry= e,
Text = d.Value
}).ToList();
return query.Select(t => new Entry
{
Id = t.entry.Id,
Name = t.Text,
CreationDate = t.CreationDate
}).ToList();
}
Is there a better way to do this without creating two lists?
Is this approach of using a Key to get translated text a best practice or am I missing the point here?
Here's a working example of your code using 2 lists instead of your tables (of course).
You can simply create the Entry in the first query. You don't need the .ToList() in that routine. Just return the IEnumerable.
using System;
using System.Collections.Generic;
using System.Linq;
namespace JoinEntities
{
class Program
{
static List<Dict> dictionary = new List<Dict>
{
new Dict
{
ID = 1,
Key = "firstEntry",
IsoCode = "en-US",
Value = "My new entry"
},
new Dict
{
ID = 2,
Key = "secondEntry",
IsoCode = "en-US",
Value = "My second entry"
},
new Dict
{
ID = 3,
Key = "firstEntry",
IsoCode = "es-ES",
Value = "Mi nueva entrada"
},
};
static List<Entry> entries = new List<Entry>
{
new Entry
{
Id = 1,
Name = "firstEntry",
CreationDate = new DateTime(2020, 11, 04)
},
new Entry
{
Id = 1,
Name = "secondEntry",
CreationDate = new DateTime(2020, 11, 07)
}
};
static void Main(string[] args)
{
var list = GetEntries("en-US");
}
public static IEnumerable<Entry> GetEntries(string isoCode)
{
return from e in entries
join d in dictionary on e.Name equals d.Key
where d.IsoCode == isoCode
select new Entry
{
Id = e.Id,
Name = d.Value,
CreationDate = e.CreationDate
};
}
}
internal class Dict
{
public int ID { get; set; }
public string Key { get; set; }
public string IsoCode { get; set; }
public string Value { get; set; }
}
internal class Entry
{
public object Id { get; set; }
public object Name { get; set; }
public object CreationDate { get; set; }
}
}
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();
In C#, I have a List where someStruct consists of a String and an int. I want to be able to find the entry matching a particular String. Is there something along the lines of IndexOf() that can do it, or do I have to use a for loop and check each entry's string field for a match?
You can use LINQ,
var yourItem = list.FirstOrDefault(entry=>entry.SomeProperty=="SomeValue"); // or list.First
You can also use the Find method of List:
var yourItem = list.Find(entry=>entry.SomeProperty=="SomeValue");
You can do that via the FindIndex method of List<T>:
var list = new List<Item>();
list.Add(new Item { Id = 1, Data = "Test1" });
list.Add(new Item { Id = 2, Data = "Test2" });
var index = list.FindIndex(x => x.Id == 2); // Second scenario FindIndex(x=> x.Data == "Test2");
Where Item is:
public struct Item
{
public int Id { get; set; }
public string Data { get; set; }
}
There isn't any need to use an index if you are looking up the string and returning the id:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Item> myList = new List<Item>() {
new Item() {Id = 123, Data = "abc"},
new Item() {Id = 124, Data = "abd"},
new Item() {Id = 125, Data = "abe"},
new Item() {Id = 126, Data = "abf"},
new Item() {Id = 127, Data = "abg"}
};
int id = myList.Where(x => x.Data == "abe").Select(y => y.Id).FirstOrDefault();
}
}
public struct Item
{
public int Id { get; set; }
public string Data { get; set; }
}
}