Check for a unique value inside a collection using LINQ? - linq

I have the following class definition:
public class PriorityItem
{
public string uniqueID { get; set; }
public List<DelegationTask> DelegationTasks = new List<DelegationTask>();
public List<int> Priorities = new List<int>();
}
Now lets say I create a list from this class like this:
List<PriorityItem> pItems = new List<PriorityItem>();
And now I want to check for the presence of an item in this list based on the uniqueID property how is this done?
For example I will populate the list with a collection of PriorityItem, but only if the Priority item does not exist. To determine if it exists, I need to check using LINQ the value of uniqueID.
Hope this makes sense.

The Any linq extension method returns true if any items are found which match a certain criteria. You can use it as follows:
string idToSearchFor = "123"
bool exists = pItems.Any(item => item.uniqueId == idToSearchFor);
With the above code exists will be true, only if your list contains a PriorityItem with the uniqueId of "123".

Exists does the same thing as Any. Exists matches the SQL parlance a little more than Any does. Any and All naturally fit together as a kind of natural qualifier pair.
However in the sentence, "do we have this unique id?" Perhaps "exists" is a better fit in terms of readability and intention.
class Class1
{
[Test]
public void Test()
{
// initialise list
var pItems = new List<PriorityItem>(){new PriorityItem(){uniqueID = "1"}, new PriorityItem(){uniqueID = "2"}, new PriorityItem(){ uniqueID = "123"}};
var IDtoFind = "123";
var wasFound = pItems.Exists(o => o.uniqueID == IDtoFind);
}
}
public class PriorityItem
{
public string uniqueID { get; set; }
//public List<DelegationTask> DelegationTasks = new List<DelegationTask>();
public List<int> Priorities = new List<int>();
}

Related

Include/Exclude fields in query with MongoDB C# driver 2.4

We have a collection contains documents in the server. Each document is like:
{ _id: "...", Prop1: "", Prop2: "", Prop3: "", LargeField: "", ... }
There're many other fields but they're not required by the client.
I want to load documents as MyDoc class whose definition is:
public class MyDoc {
public string Id { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public string LargeField { get; set; }
}
I've tried:
var client = new MongoClient(uri);
var database = client.GetDatabase("MyDatabase");
var collection = database.GetCollection<MyDocs>("MyDocs");
var allDocs = collection.Find().ToList();
Then it will load all the fields for each document, so I have to put [BsonIgnoreExtraElements] on MyDoc. The issue here is that the document is large but I only needs a limit subset of fields. Is it possible to let the driver know I only need the fields defined in the class?
If not, is it possible to exclude some of the fields like the LargeField to make the result set smaller? I've tried:
var fieldsBuilder = Builders<MyDoc>.Projection;
var fields = fieldsBuilder.Exclude(d => d.LargeField);
var allDocs = collection.Find().Project(fields).ToList();
But now allDocs becomes BsonDocument list instead of the MyDoc list. How to query MyDoc with projection?
Can someone help? It's rather simple in legacy MongoDB driver but I don't know how to do it in the new driver. Thanks.
I had a similar issue, I believe you need to specify the generic type, for some reason Project automatically assumes BsonDocument. This should fix it from BsonDocument to your class.
Change:
var allDocs = collection.Find().Project(fields).ToList();
To:
var allDocs = collection.Find<MyDoc>().Project<MyDoc>(fields).ToList();
As for how to include only certain fields, this can be done just as you are doing it with builder (using Include) or with a string in json form like:
var allDocs = collection.Find<MyDoc>().Project<MyDoc>("{Prop1: 1, Prop2: 1}").ToList();
I would highly recommend checking out projection on this guy's post:
https://www.codementor.io/pmbanugo/working-with-mongodb-in-net-part-3-skip-sort-limit-and-projections-oqfwncyka
From this article:
This brings us to another difference: with a projection definition, it implicitly converts the document type from Student to BsonDocument, so what we get back is a fluent object that, in result, will be a BsonDocument (even though what we're working with is the Student type). If we want to work with Student, we have to indicate that we still want to keep the type to Student.
A newer way:
var fieldsBuilder = Builders<MyDoc>.Projection;
var fields = fieldsBuilder.Exclude(d => d.BigField1).Exclude(d => d.BigField2);
return Collection.Find(x => x.id.Equals(id)).Project<MyDoc>(fields).ToEnumerable();
Gina
We can use FindAsync as well. and to use projection in FindSync, we have to pass the FindOptions something like this: It will select countryName, ID and population.
FindAsync loads documents one by one from DB cursor in async manner, it's a good choice when you have a huge database.
Relevant Code:
cursor = await collection.FindAsync(y => y.CountryID > 205,
new FindOptions<MyDoc, MyDoc>()
{
BatchSize = 20,
NoCursorTimeout = true,
AllowPartialResults = true,
Projection = "{'_id':1,'Name':1,'Population':1}"
}).ConfigureAwait(false);

Expression.Property(param, field) is "trolling" me [System.ArgumentException] = {"Instance property 'B.Name' is not defined for type A"}

Once again, I am facing an issue, this time with LINQ Expression builder and this time I am even struggling to find the reason why it's not working. I have a Database-First EF project with quite a few tables. For this specific case, I have to use 2 of them - DocHead and Contragent. MyService.metadata.cs looks like this:
[MetadataTypeAttribute(typeof(DocHead.DocHeadMetadata))]
public partial class DocHead
{
// This class allows you to attach custom attributes to properties
// of the DocHead class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DocHeadMetadata
{
// Metadata classes are not meant to be instantiated.
private DocHeadMetadata()
{
}
public string doc_Code { get; set; }
public string doc_Name { get; set; }
public string doc_ContrCode { get; set; }
//...
[Include]
public Contragent Contragent { get; set; }
}
}
[MetadataTypeAttribute(typeof(Contragent.ContragentMetadata))]
public partial class Contragent
{
// This class allows you to attach custom attributes to properties
// of the Contragent class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class ContragentMetadata
{
// Metadata classes are not meant to be instantiated.
private ContragentMetadata()
{
}
public string Code { get; set; }
public string Name { get; set; }
//...
I take some docHeads like this:
IQueryable<DocHead> docHeads = new MyEntities().DocHead;
Then I try to sort them like this:
docHeads = docHeads.OrderByDescending(x => x.Contragent.Name);
It is all working like I want it. I get those docHeads sorted by the name of the joined Contragent. My problem is that I will have to sort them by a field, given as a string parameter. I need to be able to write something like this:
string field = "Contragent.Name";
string linq = "docHeads = docHeads.OrderByDescending(x => x." + field + ")";
IQueryable<DocHead> result = TheBestLinqLibraryInTheWorld.PrepareLinqQueryable(linq);
Unfortunately, TheBestLinqLibraryInTheWorld does not exist (for now). So, I have set up a method as a workaround.
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(param, SortField); // normally returns x.sortField
var exp = Expression.Lambda(prop, param); // normally returns x => x.sortField
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); // normally returns sth similar to q.OrderBy(x => x.sortField)
return q.Provider.CreateQuery<T>(mce);
}
Normally... yes, when it comes to own properties of the class DocHead - those prefixed with doc_. The disaster strikes when I call this method like this:
docHeads = docHeads.OrderByField<DocHead>("Contragent.Name", true); // true - let it be Ascending order
To be more specific, the exception in the title is thrown on line 2 of the method OrderByField():
var prop = Expression.Property(param, SortField);
In My.edmx (the model), the tables DocHead and Contragent have got a relation already set up for me, which is the following: 0..1 to *.
Once again, I have no problem writing "static" queries at all. I have no problem creating "dynamic" ones via the method OrderByField(), but only when it comes to properties of the class DocHead. When I try to order by a prop of the joined Contragent class - the disaster strikes. Any help will be greatly appretiated, thank you!
The problem is that Expression.Property method does not support nested properties. It does exactly what it says - creates expression that represents a property denoted by propertyName parameter of the object denoted by the expression parameter.
Luckily it can easily be extended. You can use the following simple Split / Aggregate trick anytime you need to create a nested property access expression:
var prop = SortField.Split('.').Aggregate((Expression)param, Expression.Property);

Adding select distinct to linq query

My Web API Controller has a method that retrieves a specified number of decriptions from a database table. There are duplicate descriptions with different IDs, so sometimes the query returns duplicates when I use SELECT TOP. I also added random (ORDER BY NEWID) to lessen the chances of getting dups but duplicates still get returned sometimes. I want to change the query to SELECT DISTINCT but not sure how to do that in my particular case. Using First() seems to be complicated here. Can anyone help? My method is below:
public List<String> GetRandomDescriptions(string cat, string subcat, int n)
{
using (MyContext ctx = new MyContext())
{
var temp = ctx.Interactions.Where(d => (d.Category.Equals(cat) && d.Subcategory.Equals(subcat)))).OrderBy(d=>Guid.NewGuid()).Take(n).Select(d=>d.Description).ToList();
return temp;
}
}
Here is my class:
[Table("[Records]")]
public class Interaction
{
[Key, Column("RECORD_ID")]
public string DescId { get; set;}
public string Category { get; set; }
public string Subcategory { get; set; }
public string Description{get; set;}
}
You can use something like this
var result = ctx.Interactions
.Where(d => d.Category == cat && d.Subcategory == subcat)
.Select(d => d.Description)
.Distinct()
.Take(n)
.ToList();
The key points are - first apply the filter, then select the description, then make it distinct and finally take the required number of items.
If you really need to pick a random items, then just insert your OrderBy before Take.
You don't need to do the funny OrderBy construct. Try something like this:
public List<String> GetRandomDescriptions(string cat, string subcat, int n)
{
using (MyContext ctx = new MyContext())
{
var temp = ctx.Interactions
.Where(d => d.Category.Equals(cat) && d.Subcategory.Equals(subcat)))
.Select(d=>d.Description)
.Distinct()
.ToList();
return temp;
}
}

LINQ search Product Index

I have a List<Product> contains properties of Bikes (Name, ProductName, Color, List Price). I'm struggling to figure out how to write a search function using LINQ. I'd like to find a name of Bike. Any suggest will be help me some ways.
Imagine that your name is taken from a variable called nameToSearch.
This is if you want to get the Product.
string nameToSearch = "BikeName";
List<Product> list = bikes.Where(x => x.Name == nameToSearch).ToList();
I assume you have the following Product class:
public class Product
{
public String Name { get; set; }
public String ProductName { get; set; }
public String Color { get; set; }
public String List { get; set; }
public String Price { get; set; }
}
You also mentioned you have your data in a List<Product>. I will give a demo name for it:
List<Product> myProductList = GetProductList();
// Where GetProductList() will create a new List<Product> and populate it.
String bikeNameFilter = GetNameFilter();
// You can chnage this by the string you want for filtering.
You can use the following to get your data:
List<Product> myFilteredProductList = (from p in myProductList
where p.Name = bikeNameFilter
select p;
).ToList()
Obviously you can change the filter you want to use to another property of your product. Finally to get the actual name, you can loop through the list you just got:
foreach (var p in myFilteredProductList)
{
Console.WriteLine(p.ProductName);
// Use this value wherever you want.
}
Take a look at a nuget package I have created
http://www.nuget.org/packages/NinjaNye.SearchExtensions
This will enable the following (and more) which will return results where the search term appears in any of the properties specified
var result = products.Search("searchTerm", p => p.Name, p => p.ProductName);
Performing a search against all string properties can be done as follows:
var result = products.Search("searchTerm");
Alternatively, you can perform an AND search where the search term exists in a set of properties as follows:
string searchTerm = "searchTerm";
var result = products.Search(searchTerm, p => p.Name)
.Search(searchTerm, p => p.ProductName);
For more information take a look at the projects GitHub page or my blog posts
UPDATE: don't forget the using directive...
using NinjaNye.SearchExtensions

LINQ-To-Sharepoint Multiple content types for a single list

I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.

Resources