I want to update a collection which only contains some Id and a dictionary of objectId to objectId.
public class ME_BlaBla
{
[BsonId]
public ObjectId MyId;
public Dictionary<ObjectId, ObjectId> IdsToOtherIds;
}
Im sorry if my names aren't informative, I can't share real code =.
Now, I have this query:
var filter = Builders<ME_BlaBla>.Filter.And(
Builders<ME_BlaBla>.Filter.Eq(t => t.MyId, id),
Builders<ME_BlaBla>.Filter.Not(Builders<ME_BlaBla>.Filter.Exists(t => t.IdsToOtherIds.Values, valueId)),
Builders<ME_BlaBla>.Filter.Not(Builders<ME_BlaBla>.Filter.Exists(t => t.IdsToOtherIds.Keys, keyId)));
So, Im trying to filter by the MyId field but when I want to insert data to there I don't want duplication of any kind, Not in the Keys nor in the Values
The whole idea is that the updating must be atomic and check that neither of the provided ids are contained in the dictionary.
I'm still trying to understand how to use the Existsfilter here, so it might be the answer.
TIA.
EDIT
I changed the code to something like that: (still not sure its working well..cannot test it atm)
Builders<ME_BlaBla>.Filter.Not(Builders<ME_BlaBla>.Filter.ElemMatch(t => t.IdsToOtherIds, a => a.Key == keyId)),
Builders<ME_BlaBla>.Filter.Not(Builders<ME_BlaBla>.Filter.ElemMatch(t => t.IdsToOtherIds, a => a.Value == valueId)));
Builders<ME_BlaBla>.Filter.Not(Builders<ME_BlaBla>.Filter.Exists(t => t.IdsToOtherIds.Values, valueId))
This part of code won't check if valueId value exists in the Values property (which is a list of elements of type ObjectId) of the field Dictionary (that's what you meant, I guess). Exists checks if the document contains certain field; you could check if document of your collection have a field "Dictionary" or "Surname".
If you need a unique value in your application, could you possibly create a singleton class somewhere which will generate next (and thus unique) value of certain sequence?
Your Dictionary probably is serialized as array of documents, so if you need to check whether document already exist in the collection, you need to use AnyIn (or some other) instead of Exists.
You can look nested list existance with your criteria like that,
https://docs.mongodb.org/manual/core/read-operations-introduction/
in your state you can use your dictionaries as 2 union criteria like that,
https://docs.mongodb.org/manual/reference/operator/query/elemMatch/
cheers!
Related
My situation is like this:
A "Code" field from the source tree, needs to be mapped to a "Code" field in the destination tree. The "Code" field in the destination tree has 2 parent nodes. For the destination schema to validate, the same code must not occur more than once in the scope of the 2nd parent node. Here's an image of the hiearchy:
So within the scope of "PurchaseInformation", no same "Code" may occur. A looping functoid loops on "GoodsDescription". I've tried to create an inline C# script to handle it, but it doesn't take the scope into account. See code below:
public System.Collections.Generic.List<string> duplicateList = new System.Collections.Generic.List<string>();
public bool IsDuplicate(string code)
{
if( duplicateList.Contains(code)) {
return false;
}
else {
duplicateList.Add(code);
return true;
}
}
My problem is the global List that is created. It does not reset after each loop, but I'm unsure how to implement this functionality. My question is how I can make sure no duplicate codes are mapped within the scope of the "PurchaseInformation" record in the destination tree?
Without seeing the whole process, it's difficult to give what might be the best solution...but...
Instead of trying to reset the collection (there are reasons this is difficult) you might try a list of lists instead.
Presuming SimplifiedInvoice is an ID or something, you can use a Dictionary of Lists which will track lists of unique Code values per Invoice.
In order to filter a list of items, I'm using the type of a related property like this.
var list = Context.Items
.Include(_ => _.Details)
.Where(_ => _.Details.Kind == kind));
return list;
This returns the full listing filtered by kind, which in this case is rather bad as the Details property is large, duplicated precisely in each returned element (no two details of the same kind can ever differ) and the list of items might be quite long.
So, while using kind in Details to filter out the correct subset of items, I want the field to be empty upon returning it. One way to solve it is to obtain the IDs of all the items and then select based on that. Seems inefficient and slow. Another way is to migrate a guid into the class Item and use that for filtration. That appears duplicative and intrusive for the data model.
Is there a neat way to drop the included properties?
I'm thinking about an equivalent to the following (not working, immaginative) sample.
var list = Context.Items
.Include(_ => _.Details)
.Where(_ => _.Details.Kind == kind));
return list.Forget(_ => _.Details);
Include is only needed when you want that navigational property's data to be, well, included, in the result.
Operations translated to SQL, like GroupBy and Where, do not need the use of Include, so you can just do:
var list = Context.Items.Where(_ => _.Details.Kind == kind));
Another common error (as far as I have seen), is using Include with Select. When you use Select, all Includes are effectively thrown away.
I am struggling to solve this issue and have searched multiple ways and cannot seem to find an answer. I inherited this app from someone else and need to add a couple features to the app. I have not worked much with dictionaries and linq before, so I have been searching and trying to gain knowledge to do what I need to do.
There is a class with the following properties(removed some properties not necessary for this discussion):
class EmailRecord
{
public Dictionary<string, List<string>> emails = new Dictionary<string, List<string>>();
public string RecordID { get; set; }
[followed by additional properties and constructors...]
When the objects are created, the emails Property would have a template string in the key, and a list of strings containing email addresses in the values. For my purposes, I do not need to know what is in the key.
I have a list of EmailRecord objects called allRecords. I need to query allRecords to get a list of all EmailRecord objects where the emails dictionary property's list of values contains a specific email address I have stored in a variable called recipientEmail. The key doesn't matter, and it doesn't matter how many times the email shows up. I just need the instance of the object included in the results if the email shows up anywhere in the values of the emails property. In an instance of EmailRecord, the emails dictionary property may have two keys and within each of those keys, multiple emails in a list of strings for the value. I don't need to limit to a specific key, I just need to know if an email exists anywhere within the list of email strings anywhere in that dictionary.
I've tried a few things, with the latest being this (which doesn't work):
var results = EmailRecords
.SelectMany(x => x.emails)
.Where(x => x.Value.Contains(recipientEmail));
The above just seems to be returning the dictionary property, not the entire object.
I want to be able to loop through the results with something like this:
foreach (EmailRecord foundRecord in results) {
...do work here
}
Any thoughts or suggestions to assist me as I am trying to learn Linq? Thank you in advance for any help you can provide.
If you want to loop through EmailRecord objects which one of its emails property values contains recipientEmail, then you need to have a list of EmailRecord first. Then search throught them. following should do the trick.
List<EmailRecord> EmailRecords = new List<EmailRecord>();
//Fill the EmailRecords somewhere
foreach (KeyValuePair<string, List<string>> emailfoundRecord in
EmailRecords.emails.Where(x => x.Value.Contains(recipientEmail)))
{
//do work here
}
When you call EmailRecords.SelectMany(x => x.Emails) what you get back is an IEnumerable<KeyValuePair<string, List<string>>> or similar. Obviously this is not what you're after for your result since it strips away all that other information.
With LINQ the first thing to consider at each stage is what you are expecting to get out of the query. In this case the query results should be an enumeration of EmailRecord instances which is also what we're feeding in. Filtering that list is most simply done with the Where method, so that's where you should do all the work
Next decide on your filter criteria and write the filter predicate to suit. For any given EmailRecord we want to find out if any of the dictionary entries contains a particular email address. Since the dictionary values are lists we'll use Contains to do the actual comparison, and Any to test the dictionary itself.
Which looks like this:
var filtered = EmailRecords.Where(e =>
e.Emails.Any(kv =>
kv.Value.Contains(recipientEmail)
)
);
This works because a dictionary is also an enumerable, with each entry in the enumeration being a key/value pair.
Using Any will stop when it finds a single matching entry instead of continuing to the end of the Emails dictionary for every EmailRecord instance. If there are a lot of emails and you're expecting a high number of selections then this might save some time. Probably not however, since generally this sort of structure doesn't have a lot of duplicate email addresses in it.
Depending on how often you want to do this however it might be quicker to build a lookup and query that. Assuming that your EmailRecords list changes infrequently and you are doing a lot of this sort of lookup, you could get a large speedup.
I'll use a Dictionary<string, EmailRecord[]> for the lookup because it's (fairly) simple to build once we get a list of all of the pairs of email address and EmailRecord objects:
var emailReferences = EmailRecords.SelectMany(e =>
e.Emails.SelectMany(kv =>
kv.Value.Select(v =>
new { address = v, record = e }
)
)
);
var lookup =
emailReferences
.GroupBy(i => i.address, i => i.record)
.ToDictionary(g => g.Key, g => g.ToArray());
;
From this you will be able to locate an email address and get its referencing EmailRecord instances fairly simply and quickly:
EmailRecord[] filtered = null;
lookup.TryGetValue(recipientEmail, out filtered);
This will be faster per lookup than the LINQ equivalent above, but the setup could consume a fair amount of time and memory for large lists. If you have small or frequently changing lists (since the lookup has to be regenerated or at least invalidated at each change) then this won't improve your program's speed.
As a completely unsolicited aside, here's an extension method I use when dealing with dictionaries that have List<> as the value:
public static partial class extensions
{
public static Dictionary<TKey, List<TElem>> Add<TKey, TElem>(this Dictionary<TKey, List<TElem>> dict, TKey key, TElem value)
{
List<TElem> list;
if (dict.ContainsKey(key))
list = dict[key];
else
dict[key] = list = new List<TElem>();
list.Add(value);
return dict;
}
}
It helps make your adds simpler and easier to read.
I am using EF 6 and .net 4.5.
I have a "Documents" table with the PK "DocumentId" (Int) and another column, "Title". I have another table called "DocumentFilters" with the columns "DocumentId" (Int) and "DocGUID" (Guid). There is a FK on "Doc Id" between the two tables. It is a one to many Id's relationship between the Documents and DocumentFilters tables. The Model looks like this:
I'm trying to write a Dynamic LINQ query using the DynamicLibrary.cs library from the NuGet package.
With regular Linq, the query looks something like this in c#:
DocDBContext db = new DocDBContext();
var documents = db.Documents.Include(d => d.DocumentFilters);
Guid gTest = Guid.Parse("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
documents = documents.Where(d => d.DocumentFilters.Select(f => f.DocGUID).Contains(gTest));
This works perfectly and retrieves the documents delimited by the GUID filter that I'm interested in. However, I need to place this within a filter string dynamically, where it may contain other filters. Is it possible to do that? I've tried code like:
documents = documents.Where("DocumentFilters.Select(DocGUID).Contains(#0)", gTest);
Error: No applicable aggregate method 'Select' exists
or
documents = documents.Where("DocumentFilters.DocGUID=#0", gTest);
Error: GUID property does not exist on DocumentFilters.
(Which makes sense because DocumentFilters is a collection).
I also thought it might be worth to give this a shot:
documents = documents.Where("DocumentFilters.AsQueryable().Select(DocGUID).Contains(#0)", gTest);
But again, it doesn't look like the library supports AsQueryable and throws
Error: No applicable aggregate method 'AsQueryable' exists.
Is it possible to write such a restriction on a property of an enumerable object within the Documents table?
So one solution was to modify Dynamic.cs by adding "Select" to the IEnumerableSignatures interface in the ExpressionParser class. And additionally add it to the ParseAggregate() method:
if (signature.Name == "Min" || signature.Name == "Max")
{
typeArgs = new Type[] { elementType, args[0].Type };
}
else if (signature.Name == "Select")
{
typeArgs = new Type[] { elementType, Expression.Lambda(args[0], innerIt).Body.Type };
}
However, after this I still needed to overload the Contains() method to work with GUIDs. I didn't bother to do that. Instead I realized I can simply use the already supported .Any() instead of .Select()!
documents = documents.Where("DocumentFilters.Any(DocGUID.Equals(#0))", gTest); //!!!
I also had to change the column name from "GUID" to "DocGUID" because when writing GUID.Equals() in the restriction string, the parser mistook it for the GUID type and not the column name, and threw the error that Equals() is not a valid extension for the type GUID.
Sorry for wasting everyone's time!
Try to use Dynamic Linq, it can be used against strings.
I'm working on a .NET phone application where I have a list of items in memory that are displayed to the user. When the user clicks on an item in the user interface, they'll be brought to the "details" view for that item in a second view. I'm currently using an identity property in my class to reference unique items in the list, but I'm wondering if there is a better way.
I'm using System.Windows.Navigationto set up UriMappings like so:
<nav:UriMapping Uri="/Items/{itemId}"
MappedUri="/Views/Items.xaml?itemId={itemID}" />
I then use the NavigationService to navigate like so:
NavigationService.Navigate(new Uri("/Items/" + item.id.ToString(),
UriKind.Relative));
All of this works great, but I don't know that I like needing to include an identity column for my Item class just so that can have a unique string identifer to pass in to the MappedUri query string. My user interface control that shows the list gives me the underlying Item object in the SelectionChanged event, so I'm wondering if there's a way to "match" that Item object with a unique string value to its corresponding reference in the list. If I could provide a simple object reference to the navigation service, this would be easy, but I'm not sure how to do it with a string value. Is this what GetHashCode() is for, and if so, how do I use it correctly?
No, GetHashCode is not intended to uniquely identify objects.
The default implementation of the GetHashCode method does not guarantee unique return values for different objects. Furthermore, the .NET Framework does not guarantee the default implementation of the GetHashCode method, and the value it returns will be the same between different versions of the .NET Framework. Consequently, the default implementation of this method must not be used as a unique object identifier for hashing purposes.
It is designed for storing objects in buckets (for example in a dictionary) so that they can be quickly retrieved. The hash codes for two equal objects must be equal, but the hash codes for two different objects do not have to be different.
I think adding an Id field is a fine solution. If you don't like adding a new field just to give an object an identity you can try to find some combination of the existing fields that is guarateed to uniquely identify your object and concetenate their string representations with a suitable separator. Be careful that the separator cannot occur in any of the fields.
There's a GUID which you can use that maps to each string in the list, by calling a NewGuid method of the Guid class, one can generate a Guid and use that as a string type via ToStringmethod and use it for hashing strings.
You definitely want to provide the item's ID. Bear in mind that your application may be tombstoned, so the object may not even be in memory any more when you try to navigate to that page. You need to be able to restore the application to the page with no information other than the URL and whatever's been serialized to temporary or permanent storage (typically in OnNavigateFrom).
Using GetHashCode() for this not only doesn't guarantee uniqueness, but it's highly unlikely to work in terms of the newly restored application, too. Life gets a lot harder when you're trying to display the details of something where you haven't really got an ID... but it doesn't sound like you're really in that position.
As it looks like you've got an ID, use it as an ID. Why would you want to use anything else? If you don't have an ID but can easily add one, do so. You want a way of uniquely identifying an object... that's exactly what an ID is for.