Refer to a Property name using a Variable in Linq - c#

I am trying to access a property "DailyQtyOrders_01" in my object using the first code below, this works ok.
but now i want to use it in a linq expression on the second line code, this works, but i want the property to be variable (3rd line code), this throws error. What is the method to do that?
item.GetType().GetProperty("DailyQtyOrders_01").SetValue(item, 5);
productsSales.Where(x => x.sellerId == item.sellerId).FirstOrDefault().DailyQtyOrders_01 = 5;
productsSales.Where(x => x.sellerId == item.sellerId).FirstOrDefault().item.GetType().GetProperty("DailyQtyOrders_01").SetValue(item, 5);

First of all if this code is run in any sort of loop, you have to be careful you dont run into performance issues.
When you enter the areas of .GetType(),.GetProperty() and .SetProperty() you are doing reflection, which really shouldn't be nessesary.
It's hard to see from your code example but the Order that you have in your item should really be changable from item.DailyQtyOrders_01 directly. Also if you really want to use strings and reflection, which i cannot recommend in any way, you can at least use strongly typed ways to achieve this (which makes sure your code doesn't break on a refactor)
That would make this code:
item.GetType().GetProperty("DailyQtyOrders_01").SetValue(item, 5)
Look like this instead:
item.GetType().GetProperty(nameof(Order.DailyQtyOrders_01)).SetValue(item, 5)
available from c# 6.0 and forward, nameof can be used on classes and properties and so on to get the string, but avoiding the actual string, that you might not catch in a rename.

The problem lies in the last part of your third linq statement.
productsSales.Where(x => x.sellerId == item.sellerId).FirstOrDefault().item.GetType().GetProperty("DailyQtyOrders_01").SetValue(item, 5);
In the above code, you are assigning the value of "DailyQtyOrders_01" in an instance of "item". This is not the same instance of your linq query refers to, but the variable "item" you have created/referred above.
.GetProperty("DailyQtyOrders_01").SetValue(item, 5);
What you need to do would be to split the query into two parts, and refer the instance on which you want the value to be set.
For example
var itemInstance = productsSales.Where(x => x.sellerId == item.sellerId).FirstOrDefault().item;
itemInstance.GetType().GetProperty("DailyQtyOrders_01").SetValue(itemInstance, 5);
productsSales.Where(x => x.sellerId == item.sellerId).FirstOrDefault().item = itemInstance;

Related

Using Linq to query nested dynamic objects

I have used the following linq query to access the member Names. The following successfully returns all member Name values within the collection.
var we = CsQ.Groups.SelectMany(g => g.Members).Where(a => a.Name == "Name").Select(b => b.Value).ToList();
I want to now filter this down based another property within "Members" called AbsoluteUri which is nested in a property AgentsByUri. This doesnt work but gives an idea of the structure:
var uri = CsQ.Groups.SelectMany(g => g.Members).Where(a => a.Name == "AgentsByUri").Select(b => b.Value).//??? I NEED TO NOW ACCESS "AbsoluteUri"
How can I combine these in to one query so that I can return only the "Names" that have an "AbsoluteUri" that contains "SomeValue". AbsoluteUri seems to be nested in a collection thats nested in AgentsByUri which adds to the complication.
You can see the structure of the AgentsByUri object here - Using C# Linq to query nested objects
Excuse my terminology, I'm reasonably new to C#! Hopefully this makes sense :)
Any help or guidance VERY appreciate :)
EDIT3
Getting somewhere! Casting as dynamic partially working - member.AgentsByUri is now OK, just cant figure out how to make it apply to the rest of the query. Tried adding in various locations by no luck.
EDIT2
Thanks for everyone's input. I haven't had any further success. I think the biggest problem is that I am dealing with a PowerShell object which is dynamically generated at run time. As a result I cannot access the classes/object because the compiler doesn't yet no about them. To get around this I use the "dynamic" type which allows the compiler to trust that what I provide will be valid at run time. Can I cast as dynamic in a linq query? Or do I need to go about this in a different way?
Heres what I get with the examples give:
EDIT1 (click and zoom, image is high res):
I'm just writing this down, based on the image you provided. However, I can not check for correctness without the surrounding code, so no guarantees.
var uri = CsQ.Groups
.SelectMany(g => g.Members)
.Where(m => m.AgentsByUri.SelectMany(a => a.Value, (a, v) => v.AbsoluteUri).Contains("SomeValue"))
.Select(b => b.Value)
In LINQ syntax it should be much clearer
var uri =
from group in CsQ.Groups
from dynamic member in group.Members
where
(from agent in (IEnumerable<dynamic>)member.AgentsByUri
where agent.Name = "AgentsByUri" // this line may be redundant
from x in (IEnumerable<dynamic>)agent.Value
select x.AbsoluteUri).Contains("SomeValue")
select member.Value;
EDIT: The suggestion in my second comment does not quite work. I changed the code in LINQ syntax above to account for dynamic objects as the source by explicitly casting to IEnumerable<dynamic>. Note however that the cast will fail for an enumeration of a value type.
I hope this will work for you.
It's hard to give a proper answer without having a proper overview of the class structure. I guess this might work:
var uri = CsQ.Groups.SelectMany(g => g.Members).Where(a => a.Name == "AgentsByUri").Select(b => b.Value).Where(x => x.AbsoluteUri == "SomeValue");
I had to do some digging and make a few assumptions here:
You want to return a list of PSMemberInfo.
PsMemberInfo.Name must equal "AgentsByUri".
In the Value property (which is a collection of Uri), you want to filter this collection on items whose AbsoluteUri property equals "SomeValue".
var we = CsQ.Groups.SelectMany(g => g.Members).Where(member => member.Name == "AgentsByUri" && member.Value != null && member.Value.Any(uri => uri.AbsoluteUri == "SomeValue")).ToList();

Is there any way to make right to left value assigning in linq or in any other way

The title is pretty unclear. But I couldn't find the proper words. Generally Linq works in the below syntax
MyList.Where().Select(x => {MyFunction(x);})
It is good in ordinary conditions but in some situation like in my case. I am creating a tree structure using dictionary. In this if I want to add a set
Set.Foreach(x => {(MyDict[logEvent.level][logEvent.event][logEvent.subevent][logEvent.filePath]).Add(x);});
But it would be nice if I can do like below
(MyDict[logEvent.level][logEvent.event][logEvent.subevent][logEvent.filePath]).Add(MySet.Foreach(x => {return x;}));
Is there any way possible to dothis ?
You can do it, if object stored in Dict has AddRange method which accepts IEnumerable<T>. But you should ski[ ForEach and just pass MySet:
MyDict[logEvent.level][logEvent.event][logEvent.subevent][logEvent.filePath]).AddRange(MySet);

DbSortClause expressions must have a type that is order comparable parameter Name :Key

I am using Linq to entity and have the following query
IQueryable<DomainModel.User> userResult =
userResult.OrderBy(u => u.UserClientRoles.OrderBy(r => r.Role.RoleName));
But I am getting this error
DbSortClause expressions must have a type that is order comparable
parameter Name :Key
and it returns an empty collection.
Any idea what's going on?
.OrderBy(), when working with databases, is supposed to take in a delegate that returns only a single property that represents a column in your database. I'm not sure what you're trying to do, but it looks like
u.UserClientRoles.OrderBy(r => r.Role.RoleName)
Will return an enumeration of values, which can't be ordered.
I had the same problem, I solved it using this:
your code:
IQueryable<DomainModel.User> userResult = userResult.OrderBy(u => u.UserClientRoles.OrderBy(r => r.Role.RoleName));
my code:
List<Membership> results = new List<Membership>();
results.AddRange(memberships.OrderBy(m => m.Roles));
memberships = results.AsQueryable();
coincidences:
*.OrderBy(m => m.Roles)
solution:
*.OrderBy(m => m.Roles.Select(r => r.RoleId).FirstOrDefault())
possible problem's reason:
Maybe, you did what I did, and cause that 1 user/member could have more than 1 role in the same membership. That made a conflict with/to OrderBy() because the application can just "order" a single element at the time, when she call the Role (which is an ICollection of elements) the instead receive more than 1 element with no kind of priority's levels (even when we could assume that the application will take the role's index as priority's base level, actually its don't).
solution's explaination:
When you add the *.Select(r => r.RoleId), you are specifying to the application which element will be used to OrderBy(). But, as you shall see when you maybe reached at this point, just by using the *.Select(r => r.RoleId) could be not enough, because the application is still receiving multiple results with the same priority's level. Adding *.Select(r => r.RoleId).FirstOrDefault() you are basically saying: "...I don't care how many results you received from that element, just the focus on the first result, or order them by its default..." (default normally means EMPTY or NULL).
additional information:
I used non-official's simple concepts/meanings to explain a complex solution with simple words, which means that you could maybe have problems to find similar posts in the web by using the words/concepts used in this "answer". Otherwise, the code itself works and you shouldn't not have any problem by applying it and/or modifying it by yourself. GOOD LUCK!!! (^_^)
In my case, I was accidentally trying to order by an object instead of ordering by one of it's properties.
You should you use
var query = from Foo in Bar
orderby Foo.PropertyName
select Foo;
Instead of
var query = from Foo in Bar
orderby Foo
select Foo;
Note: you will get the same behaviour event if there is an override on Foo's ToString() method.

FindAll in a list of custom objects

Well, I got an object called Mamamia and inside of it has some string properties. I created a list of this object and populated it with 150 items.
I'm trying to use List.FindAll but I reaaally don't know how to do it. I've tried this way:
produto = products.FindAll(delegate(Mamamia cv) {return cv.LocalPackage.Remove(1,21) == cmbPackage.SelectedValue};
I don't know why the delegate is there, I just tried to copy from some other code on the internet.
Thanks in advance!
The delegate is there to see whether the value that you're testing is what you're looking for. The call to Remove looks worryingly like it's mutating the value though - that's rarely a good thing when you're looking through the list. I guess if it's a string then it's not too bad, although it may not be what you're after...
What are the types involved, and what are you looking for? Oh, and are you using C# 3 and/or .NET 3.5? That would make it easier (even C# 3 against .NET 2.0 means you could use a lambda expression instead of an anonymous method).
What's happening when you run the code at the moment? If it's just not finding anything, it may just be because you're testing for reference equality (if SelectedValue returns object).
Try this:
produto = products.FindAll(delegate(Mamamia cv) {
return cv.LocalPackage.Remove(1,21).Equals(cmbPackage.SelectedValue);
});
EDIT:
It sounds like you only want a single value, and if you're using .NET 3.5 it would be more idiomatic to use LINQ in the first place. I would use:
string selectedText = (string) cmbPackage.SelectedValue;
Mamamia item = products.FirstOrDefault
(cv => cv.LocalPackage.Remove(1,21) == selectedText);
if (item != null)
{
// Found it; otherwise item will be null
}

Common problem for me in C#, is my solution good, stupid, reasonable? (Advanced Beginner)

Ok, understand that I come from Cold Fusion so I tend to think of things in a CF sort of way, and C# and CF are as different as can be in general approach.
So the problem is: I want to pull a "table" (thats how I think of it) of data from a SQL database via LINQ and then I want to do some computations on it in memory. This "table" contains 6 or 7 values of a couple different types.
Right now, my solution is that I do the LINQ query using a Generic List of a custom Type. So my example is the RelevanceTable. I pull some data out that I want to do some evaluation of the data, which first start with .Contains. It appears that .Contains wants to act on the whole list or nothing. So I can use it if I have List<string>, but if I have List<ReferenceTableEntry> where ReferenceTableEntry is my custom type, I would need to override the IEquatable and tell the compiler what exactly "Equals" means.
While this doesn't seem unreasonable, it does seem like a long way to go for a simple problem so I have this sneaking suspicion that my approach is flawed from the get go.
If I want to use LINQ and .Contains, is overriding the Interface the only way? It seems like if there way just a way to say which field to operate on. Is there another collection type besides LIST that maybe has this ability. I have started using List a lot for this and while I have looked and looked, a see some other but not necessarily superior approaches.
I'm not looking for some fine point of performance or compactness or readability, just wondering if I am using a Phillips head screwdriver in a Hex screw. If my approach is a "decent" one, but not the best of course I'd like to know a better, but just knowing that its in the ballpark would give me little "Yeah! I'm not stupid!" and I would finish at least what I am doing completely before switch to another method.
Hope I explained that well enough. Thanks for you help.
What exactly is it you want to do with the table? It isn't clear. However, the standard LINQ (-to-Objects) methods will be available on any typed collection (including List<T>), allowing any range of Where, First, Any, All, etc.
So: what is you are trying to do? If you had the table, what value(s) do you want?
As a guess (based on the Contains stuff) - do you just want:
bool x= table.Any(x=>x.Foo == foo); // or someObj.Foo
?
There are overloads for some of the methods in the List class that takes a delegate (optionally in the form of a lambda expression), that you can use to specify what field to look for.
For example, to look for the item where the Id property is 42:
ReferenceTableEntry found = theList.Find(r => r.Id == 42);
The found variable will have a reference to the first item that matches, or null if no item matched.
There are also some LINQ extensions that takes a delegate or an expression. This will do the same as the Find method:
ReferenceTableEntry found = theList.FirstOrDefault(r => r.Id == 42);
Ok, so if I'm reading this correctly you want to use the contains method. When using this with collections of objects (such as ReferenceTableEntry) you need to be careful because what you're saying is you're checking to see if the collection contains an object that IS the same as the object you're comparing against.
If you use the .Find() or .FindAll() method you can specify the criteria that you want to match on using an anonymous method.
So for example if you want to find all ReferenceTableEntry records in your list that have an Id greater than 1 you could do something like this
List<ReferenceTableEntry> listToSearch = //populate list here
var matches = listToSearch.FindAll(x => x.Id > 1);
matches will be a list of ReferenceTableEntry records that have an ID greater than 1.
having said all that, it's not completely clear that this is what you're trying to do.
Here is the LINQ query involved that creates the object I am talking about, and the problem line is:
.Where (searchWord => queryTerms.Contains(searchWord.Word))
List<queryTerm> queryTerms = MakeQueryTermList();
public static List<RelevanceTableEntry> CreateRelevanceTable(List<queryTerm> queryTerms)
{
SearchDataContext myContext = new SearchDataContext();
var productRelevance = (from pwords in myContext.SearchWordOccuranceProducts
where (myContext.SearchUniqueWords
.Where (searchWord => queryTerms.Contains(searchWord.Word))
.Select (searchWord => searchWord.Id)).Contains(pwords.WordId)
orderby pwords.WordId
select new {pwords.WordId, pwords.Weight, pwords.Position, pwords.ProductId});
}
This query returns a list of WordId's that match the submitted search string (when it was List and it was just the word, that works fine, because as an answerer mentioned before, they were the same type of objects). My custom type here is queryTerms, a List that contains WordId, ProductId, Position, and Weight. From there I go about calculating the relevance by doing various operations on the created object. Sum "Weight" by product, use position matches to bump up Weights, etc. My point for keeping this separate was that the rules for doing those operations will change, but the basic factors involved will not. I would have even rather it be MORE separate (I'm still learning, I don't want to get fancy) but the rules for local and interpreted LINQ queries seems to trip me up when I do.
Since CF has supported queries of queries forever, that's how I tend to lean. Pull the data you need from the db, then do your operations (which includes queries with Aggregate functions) on the in-memory table.
I hope that makes it more clear.

Categories

Resources