using Linq Except method on SPFieldUserValueCollection - linq

I'm new to SharePoint and faced the problem below:
I have an EventReceiver with an ItemUpdating method where I can get values from a multivalue user field with BeforeProperties and AfterProperties.
I would like to get the differences and store them in variables onlyBefore and onlyAfter SPFieldUserValueCollections.
onlyBefore should be equal to values that are only in the beforeChangeValues.
onlyAfter should be equal to values that are only in the afterChangeValues.
Excerpt of my code:
SPWeb web = properties.OpenWeb();
SPFieldUserValueCollection beforeChangeValues = properties.ListItem["HumanResources"] as SPFieldUserValueCollection;
SPFieldUserValueCollection afterChangeValues = new SPFieldUserValueCollection(web, properties.AfterProperties["HumanResources"].ToString());
I would like to get the differences of two SPFieldUserValueCollections but the onlyAfter and onlyBefore are always null
IEnumerable<SPFieldUserValue> onlyBefore = beforeChangeValues.Except(afterChangeValues) as SPFieldUserValueCollection;
IEnumerable<SPFieldUserValue> onlyAfter = afterChangeValues.Except(beforeChangeValues) as SPFieldUserValueCollection;
Unfortunately onlyBefore and onlyAfter are always null.
Can you help me with how should I use the Except method to get the difference from two SPFieldUserValueCollections?
Or is it a completely wrong solution and I should convert and work with arrays instead of collections?

Related

Using Spring MongoTemplate to update nested arrays in MongoDB

Can anyone help with a MongoTemplate question?
I have got a record structure which has nested arrays and I want to update a specific entry in a 2nd level array. I can find the appropriate entry easier enough by the Set path needs the indexes of both array entries & the '$' only refers to the leaf item. For example if I had an array of teams which contained an array of player I need to generate an update path like :
val query = Query(Criteria.where( "teams.players.playerId").`is`(playerId))
val update = Update()
with(update) {
set("teams.$.players.$.name", player.name)
This fails as the '$' can only be used once to refer to the index in the players array, I need a way to generate the equivalent '$' for the index in the teams array.
I am thinking that I need to use a separate Aggregate query using the something like this but I can't get it to work.
project().and(ArrayOperators.arrayOf( "markets").indexOf("")).`as`("index")
Any ideas for this Mongo newbie?
For others who is facing similar issue, One option is to use arrayFilters in UpdateOptions. But looks like mongotemplate in spring does not yet support the use of UpdateOptions directly. Hence, what can be done is:
Sample for document which contain object with arrays of arrayObj (which contain another arrays of arrayObj).
Bson filter = eq("arrayObj.arrayObj.id", "12345");
UpdateResult result = mongoTemplate.getDb().getCollection(collectionName)
.updateOne(filter,
new Document("$set", new Document("arrayObj.$[].arrayObj.$[x].someField"), "someValueToUpdate"),
new UpdateOptions().arrayFilters(
Arrays.asList(Filters.eq("x.id, "12345))
));

Order by a string by numbers with QueryExpression

I'm having a problem ordering numbers that are saved as string in CRM,
Its working fine until 10, then it says that 9 > 10 I know a simple solution where I can append zeros to the strings into a fixed length.
Wondering if there is a way to order by a string by int in some way.
My code:
QueryExpression query = new QueryExpression(entity);
query.ColumnSet.AddColumn(ID);
query.AddOrder(ID, OrderType.Descending); //there is a problem because the type is string.
EntityCollection entityCollection = organizationService.RetrieveMultiple(query);
I don't think there is any easy way of achieving this. I faced the same issue for the post code and ended up storing both values i.e. string and int. And while querying i used the int field to sort it.
Hope this helps,
Ravi Kashyap
Another possible solution to this problem would be to sort the results of the query using LINQ's OrderBy() method instead of using QueryExpression's built in ordering.
EntityCollection results = _client.RetrieveMultiple(query);
var sortedResults = results.Entities.OrderBy((e) =>
int.Parse(e.GetAttributeValue<string>("nameofattribute"))
);
This will yield the results your looking for. It isn't an ideal solution but at least you don't have to store everything twice.

Querying MVC collection with Array

Im using entity framework 4 and linq/lamda expressions. Im sure its an easy one but im trying to query a collection with an array but get records which contain all the arrays values.
basically what im doing is this
var records = collection.where(x.classifications.Any(y=> Array.Contains(y.ClassificationID))).ToList()
This works in a sense that it returns records that contain any of the arrays values but how do i get only records that contain all of the values in the array.
Hope that makes sense
EDIT:
Im marking the comment below as the answer as I did have to use ALL in my query to get it to work, however I also had to re - write my query slightly. This is what i eventually had...
var records = collection.Where(x=> Array.All(c=> x.Classifications.Select(l=>l.ClassificationID).Contains(c)))
What about using All instead of Any?
var records = collection.Where(x => x.classifications.All(y => Array.Contains(y.ClassificationID)))
.ToList()

Entity Framework - Linq - Unable to create a constant value of type ‘System.Object’. Only primitive types

I have a method to build an expression for a linq query for a given type, property, and value. This works wonderfully as long as the property on the type is NOT nullable. Here is the example I am working from (http://www.marcuswhitworth.com/2009/12/dynamic-linq-with-expression-trees) I am calling the Equals method on the property. However I have discovered that the Equals method for Nullable types takes an Object as a parameter instead of the Nullable type. I attempted to use GetValueOrDefault to hide the null values but EF doesn't support that method. As a simple example the following code will throw an error:
decimal? testVal = new decimal?(2100);
var test = (from i in context.Leases where i.AnnualRental.Equals(testVal) select i).ToList();
However if you use == instead of the Equals() method it will work OK. I am not sure how to convert the code to use == instead of Equals() however. Any suggestions will be greatly appreciated.
If you say ==, you generate a BinaryExpression with NodeType as ExpressionType.Equal. http://msdn.microsoft.com/en-us/library/bb361179.aspx
If you say .Equals(x), you generate a MethodCallExpression. The MethodCallExpressions that LinqToEntities may translate into Sql is a limitted list (for example none of your own undecorated methods are in that list). Nullable<T>.Equals(x) is apparently not on that list.
Don't say .Equals(x) to LinqToEntities.

How to instantiate an object within a linq query

This is kinda theoretical question,
I was looking at someone else' code (below) and my simple solution was to instantiate the collection outside linq, but I can guess there will be cases where I'd want to instantiate the objects inside the query, and perhaps only on a selection of elements.
Here's a simplified example of how this was being done (badly).
var pods = (from n in ids
where new Node(Convert.ToInt32(n)).HasValue("propertyName")
select new
{
Id = Convert.ToInt32(n),
Url = new Node(Convert.ToInt32(n)).Url,
Name = new Node(Convert.ToInt32(n)).Title()
}).ToList();
Irrelevant Note: in this case the Node constructor is getting data from a memory cache.
How can I improve this example to only instantiate each object once using linq?
Cheers.
Murray.
Use a let clause like this:
var pods = (
from n in ids
let id = Convert.ToInt32(n)
let node = new Node(id)
where node.HasValue("propertyName")
select new
{
Id = id,
Url = node.Url,
Name = node.Title()
}
).ToList();
For more information please see let clause (C# Reference):
In a query expression, it is sometimes
useful to store the result of a
sub-expression in order to use it in
subsequent clauses. You can do this
with the let keyword, which creates a
new range variable and initializes it
with the result of the expression you
supply. Once initialized with a value,
the range variable cannot be used to
store another value. However, if the
range variable holds a queryable type,
it can be queried.

Resources