I have found some threads with similar titles, but none seem to have a fitting answer.
One mentioned a bug in .NET versions prior to 4.0 - I use 4.0, so this should not be it.
Consider the example:
I am attempting to create a collection of instances of class Part, which do not belong to any instance of class PartGroup.
Func<Part,bool> hasGroup = P => partColl.Groups.Any( G => G.Parts.Contains(P) );
var groupless = partColl.Parts.Where( P => ! hasGroup(P) );
partColl is an instance of a class implementing the properties Groups and Parts, each IEnumerable<T> where T is PartGroup or Part respectively, internally implemented as List<T>.
partColl.Parts contains all parts in existence.
class Group has property IEnumerable<Part> Parts, listing references to parts belonging to the group.
In my current program, there are 27 parts and 5 groups with no overlap in elements.
Nothing that should trouble the stack, despite quadratic complexity, if something wasn't foul.
When I run this, it will crash with said exception on hasGroup.
What am I missing?
EDIT:
A little detail slipped my abstraction presented here:
IEnumerable PartGroup.Parts was, unlike the two properties of PartCollection, not backed by a List, it was an auto-property with private set, initialized in the c'tor with a passed-in IEenumerable. The instance behind this IEnumerable is also a list, and an own one for each group, so I'm not sure yet what is going on exactly.
BUT, the exception is gone by: also backing that property with a variable of type List and in the constructor, assigning to it: _list = parts.ToList(), where parts is IEnumerable<Parts>, passed as a param to the ctor. I did ToList just to make sure it will really be a list, not some half baked query, but it should, since at the place where I am building the groups, one new List for each group is allocated before passing it in...
The remaining question is still interesting: What was going on, why did the auto-property 'cause' the exception?
I'll post more details on request, but gotta go for a while now.
This small sample does not reproduce the issue.
using System;
using System.Linq;
using System.Collections.Generic;
class P
{
class PartGroup
{
public List<Part> Parts { get; private set; }
public PartGroup()
{
Parts = new List<Part>();
}
}
class Part
{
}
class PartCollection
{
public List<Part> Parts { get; set; }
public List<PartGroup> Groups { get; set; }
public PartCollection()
{
Parts = new List<Part>();
Groups = new List<PartGroup>();
}
}
static void Main()
{
var groups = new List<PartGroup> { new PartGroup(), new PartGroup(), new PartGroup(), new PartGroup(), new PartGroup() };
var partColl = new PartCollection();
partColl.Parts.Add(new Part());
partColl.Groups.AddRange(groups);
for (int i = 0; i < 27; i++)
{
var part = new Part();
groups[i % groups.Count].Parts.Add(part);
partColl.Parts.Add(part);
}
partColl.Parts.Add(new Part());
Func<Part, bool> hasGroup = P => partColl.Groups.Any(G => G.Parts.Contains(P));
var groupless = partColl.Parts.Where(P => !hasGroup(P)).ToList();
}
}
Related
I have this class:
public class UserSet
{
public int One { get; set; }
public string Two { get; set; }
public string Three { get; set; }
}
And a static class:
public static class AllUsers
{
public static List<UserSet> Usersc = new List<UserSet>();
}
I always create new UserSet and then store it in Usersc. Basically I do this:
Data.UserSet setupSet = new Data.UserSet()
{
One = name,
Two = OsCislo,
Three = User.Identity.Name
};
Then I store it.
Data.AllUsers.Usersc.Add(setupSet)
Now what I want to do is:
Define setupSet
Check if setupSet is in Usersc.
If one (from my setupSet) is in Usersc then
3a) if setupSet is in Userc and everything is equal then don't do anything.
3b) If something is different then delete old UserSet in AllUsers and insert the new one
In code it will look like this:
Data.UserSet setupSet = new Data.UserSet()
{
One = name,
Two = OsCislo,
Three = User.Identity.Name
};
if (Data.AllUsers.Usersc.Select(s => s.One).ToList().Contains(User.Identity.Name))
{
// Always returns false for some reason
if (!Data.AllUser.Usersc.Contains(setupSet))
{
Data.AllUsers.Usersc.Remove(Data.AllUsers.Usersc.Where(s => s.One == User.Identity.Name).ToList().First());
Data.AllUsers.Usersc.Add(setupSet);
}
}
else
{
Data.AllUsers.Usersc.Add(setupSet);
}
As you can see, problem is in my if statement where I try to get whether setupSet is inside Usersc but for some odd reason it always returns false even though setupSet should be inside it.
This is because your line of code Data.AllUser.Usersc.Contains(setupSec) compares references - not values of the properties of objects in the list.
You create your setupSet object at the top without adding it to the list - so this list doesn't contain it. Despite the fact that two objects have exactly the same values of properties for .net these are two different objects. Therefore list.Contains() return false.
So to solve your problem there are 2 ways:
Don't use Contains() method of List - and use LINQ expression comparing properties that are crucial for your understanding equality of 2 objects of your UserSet class
Definitely better - implement Equals method inside your UserSet class that will tell the CLI what do you understand as EQUAL if it comes to your class.
Take a look at the following code (please bear in mind that this is a naive implementation):
void Main()
{
var myList = new List<SomeClass>();
var o1 = new SomeClass{SomeProperty = "foo"};
myList.Add(o1);
var o2 = new SomeClass{SomeProperty = "foo"};
if(myList.Contains(o2))
{
"List Contains element".Dump();
}
else{
"List does not contain element".Dump();
}
}
private class SomeClass{
public string SomeProperty {get;set;}
public override bool Equals(object obj)
{
var casted = obj as SomeClass;
if(casted == null) return false;
if(string.Equals(casted.SomeProperty, this.SomeProperty)) return true;
return false;
}
}
Here I told the .NET what it means for me that 2 instances of my SomeClass are equal by overriding the {public bool Equals(object obj)} method.
Inside of that method I write that 2 objects are equal if value of SomeProperty is the same.
if(string.Equals(casted.SomeProperty, this.SomeProperty)) return true;
Therefore in the console, I can see "List Contains element". If you remove that override Equal the console will say "List does not contain the element".
Last but not least as Blindy wrote - use thread-safe collection. It will make your example better (however I don't believe that threads are the problem that you described).
You can also dive deeper into in-memory database in Entity Framework - you will not have to create that static class with a static collection of elements (which is a mock of database of cache I believe). It is super-easy
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "My_DB")
.Options;
var context = new ApplicationDbContext(options);
even tho setupSet should be inside it
There's a reason static is rarely used in Asp.Net projects, of any kind: there are multiple threads that handle your requests, and if they're all fighting over the same List<>, that's a recipe for disaster.
Either use a synchronized collection (like ConcurrentSet<> or ConcurrentBag<>), or use a crude locking mechanism, or something to make sure you don't trample one thread's list access from another.
Or even better, use a database to store data. That's what it's made for, and most DBMS are transactional, so they handle multi-threaded access safely for you.
I don't know why item in list has been changed when i using lambda expression.
here my code
using System;
using System.Collections.Generic;
using System.Linq;
public class test
{
public string id { get; set; }
public string name { get; set; }
public string no { get; set; }
}
public class Program
{
public static List<test> a;
public static List<test> b = new List<test>();
public static List<test> a_list()
{
List<test> c = new List<test>();
c.Add(new test() { id = "_01", name=null, no="1"});
c.Add(new test() { id = "_02", name=null, no="2"});
return c;
}
public static void Main()
{
a = a_list();
string key = "_01";
//
test i = new test();
i = a.First(x => x.id.Equals(key));
i.name = "xxxxxxx";
b.Add(i);
//
Console.WriteLine("id:"+a[0].id+"\nName:"+a[0].name+"\nno:"+a[0].no);
Console.WriteLine("id:"+b[0].id+"\nName:"+b[0].name+"\nno:"+b[0].no);
Console.ReadLine();
}
}
This is result
id:_01
Name:xxxxxxx
no:1
id:_01
Name:xxxxxxx
no:1
Why a[0] equals "xxxxxxx"? (sorry my english is bad )
here:
i = a.First(x => x.id.Equals(key));
i.name = "xxxxxxx";
i and a.First() are now references to the same object. Modifying a property on that object will affect both of these, because they are pointers to the same thing.
Basically what your code does is:
It creates a list with two elements (a)
It takes its first element and modifies that element's value
It takes that element and adds it to the second list (b)
So the outcome is that you have two lists - one (a) contains two elements, the other (b) just one, but (and here's the important bit) - it's the exact same element as the first one in a. So when you write the first element of both lists, the results are equal.
Imagine you have a camera pointing at a car. When the car door opens, you see it through the camera, but the camera is not the car.
Now your friend some distance away also points his camera at the car. You both can see the door open and close, and the lights, etc.
A variable is like the camera and the car is the object it points to.
If you turn off or destroy one of the cameras, the car is still there. In fact, if you destroy both cameras the car is still there (until garbage collection).
See this excellent article: http://codebetter.com/karlseguin/2008/04/28/foundations-of-programming-pt-7-back-to-basics-memory/
I'm doing a CollectionAssert for two public lists built from linq-to-sql queries. When I do the CollectionAssert for these, it always fails even for a test set which clearly should pass.
I even did a quick test in my code to make sure I'm using the CollectionAssert correctly.
//This always passes
List<string> testList1 = new List<string>();
List<string> testList2 = new List<string>();
testList1.Add("one");
testList2.Add("two");
testList2.Add("three");
testList2.Add("one");
CollectionAssert.IsSubsetOf(testList1, testList2);
//This always fails
CollectionAssert.IsSubsetOf(memberOrganizations, member_Dim_Organization, "Error Matching Fields");
Any thoughts on how I can correct the syntax of the second assert? I've attached a screen shot of my debug window. Debug Screen Shot
I define the format of structure of the list in one method
namespace DefineStructure
{
public class Results
{
public string OrgID { get; set; }
}
This load the data in a separate
namespace Reporting.Member_Dim_Organization.ETL.QA
{
public class LoadData
{
public static cDataContext oMain = new cDataContext();
public static List<Results> oecMainResults = (from mo in oecMain.MemberOrganizations
orderby mo.OrgID
select new Results
{
OrgID = (mo.OrgID).ToString() }).Take(1).ToList();
Then I declare it in my main program
namespace Reporting.Main
{
[Binding]
public class Member_Dim_OrganizationsSteps
{
public static List<Results> memberOrganizations = LoadData.oecMainResults;
public static List<Results> member_Dim_Organization = LoadData.reportingMasterResults;
Does that help clarify?
Thanks for helping an old dog who's trying to learn some new tricks.
You are assuming that if the two collections have instances with the same data that they are equal, which is not the case by default. It sounds like you need to override Equals and GetHashCode on the Results class to define "equality".
There are lots of examples out there of defining equality for custom types, most of which involve defining the hash code as a combination of the hash codes of the fields that define equality.
I am trying to learn C# by making a simple program that shows the user sushi rolls given their desired ingredients. i.e. a user wants a roll with crab, and the program will spit out a list of sushi rolls that contain crab.
I've created a Roll class
public class Roll
{
private string name;
private List<string> ingredients = new List<string>();
}
With some getters and setters and other various methods.
In the GUI, I have some checkboxes which each call an update() method from the Control class, which will then need to check a list of rolls against a list of ingredients given by the GUI checkboxes. What I have is this
class Controller
{
static List<Roll> Rolls = new List<Roll>();
static RollList RL = new RollList();
static List<String> ingredients = new List<String>();
static Roll roll = new Roll();
}
public void update
{
foreach(Roll roll in Rolls)
{
foreach (String ingredient in ingredients)
if (!roll.checkForIngredient(ingredient))
Rolls.Remove(roll);
}
}
But a System.InvalidOperationException is thrown saying that because the collection was modified, the operation can't execute. OK, that's fair, but then what's the best way to do this? Here on Stack Overflow there's a post about removing elements from a generic list while iterating over it.
This was good and pointed me in the right direction, but unfortunately, my predicate condition simply doesn't match the top answer's.
It would have to iterate over the ingredients list, and I'm not even sure that's possible...
list.RemoveAll(roll => !roll.containsIngredient(each string ingredient in ingredients) );
shudder
I've tried the for loop, but I can't seem to get the enumeration to work either, and I wonder if it's even necessary to enumerate the class for just this method.
So I come here to try and find an elegant, professional solution to my problem. Keep in mind that I'm new to C# and I'm not all too familiar with predicate logic or enumeration on classes.
To use RemoveAll you can rewrite your condition to this:
list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
This exploits the fact that when the compiler sees this, it will effectively rewrite it to this:
list.RemoveAll(roll => !ingredients.All(i => roll.checkForIngredient(i)));
Which is what you want. If not all the ingredients are present, remove the roll.
Now, having said that, since you say you're a beginner, perhaps you feel more comfortable keeping your loop, if you could just make it work (ie. stop crashing due to modifying the loop). To do that, just make a copy of the collection and then loop through the copy, you can do this by just modifying the foreach statement to this:
foreach(Roll roll in Rolls.ToList())
This will create a list based copy of the Rolls collection, and then loop on that. The list will not be modified, even if Rolls is, it is a separate copy containing all the elements of Rolls when it was created.
As requested in the comments, I'll try to explain how this line of code works:
list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
The RemoveAll method, which you can see the documentation for here takes a predicate, a Predicate<T>, which is basically a delegate, a reference to a method.
This can be a lambda, syntax that creates an anonymous method, using the => operator. An anonymous method is basically a method declared where you want to use it, without a name, hence the anonymous part. Let's rewrite the code to use an anonymous method instead of a lambda:
list.RemoveAll(delegate(Roll roll)
{
return !ingredients.All(roll.checkForIngredient);
});
This is the exact same compiled code as for the lambda version above, just using the bit more verbose syntax of an anonymous method.
So, how does the code inside the method work.
The All method is an extension method, found on the Enumerable class: Enumerable.All.
It will basically loop through all the elements of the collection it is extending, in this case the ingredients collection of a single roll, and call the predicate function. If for any of the elements the predicate returns false, the result of calling All will also be false. If all the calls return true, the result will also be true. Note that if the collection (ingredients) is empty, the result will also be true.
So let's try to rewrite our lambda code, which again looked like this:
list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
Into a more verbose method, not using the All extension method:
list.RemoveAll(delegate(Roll roll)
{
bool all = true;
foreach (var ingredient in ingredients)
if (!roll.checkForIngredient(ingredient))
{
all = false;
break;
}
return !all;
});
This now starts to look like your original piece of code, except that we're using the RemoveAll method, which needs a predicate that returns whether to remove the item or not. Since if all is false, we need to remove the roll, we use the not operator ! to reverse that value.
Since you are both new to C# but also asked for an elegant solution, I will give you an example of how to solve this using a more object-oriented approach.
First of all, any "thing" of significance should be modeled as a class, even if it has just one property. This makes it easier to extend the behavior later on. You already defined a class for Roll. I would also add a class for Ingredient:
public class Ingredient
{
private string _name;
public string Name
{
get { return _name; }
}
public Ingredient(string name)
{
_name = name;
}
}
Note the Name property which only has a getter, and the constructor which accepts a string name. This might look like unnecessary complexity at first but will make your code more straightforward to consume further down the road.
Next, we'll modify your Roll class according to this guideline and give it some helper methods that make it easier for us to check if a roll contains a certain (list of) ingredients:
public class Roll
{
private string _name;
private List<Ingredient> _ingredients = new List<Ingredient>();
public string Name
{
// By only exposing the property through a getter, you are preventing the name
// from being changed after the roll has been created
get { return _name; }
}
public List<Ingredient> Ingredients
{
// Similarly here, you are forcing the consumer to use the AddIngredient method
// where you can do any necessary checks before actually adding the ingredient
get { return _ingredients; }
}
public Roll(string name)
{
_name = name;
}
public bool AddIngredient(Ingredient ingredient)
{
// Returning a boolean value to indicate whether the ingredient was already present,
// gives the consumer of this class a way to present feedback to the end user
bool alreadyHasIngredient = _ingredients.Any(i => i.Name == ingredient.Name);
if (!alreadyHasIngredient)
{
_ingredients.Add(ingredient);
return true;
}
return false;
}
public bool ContainsIngredients(IEnumerable<Ingredient> ingredients)
{
// We use a method group to check for all of the supplied ingredients
// whether or not they exist
return ingredients.All(ContainsIngredient);
// Could be rewritten as: ingredients.All(i => ContainsIngredient(i));
}
public bool ContainsIngredient(Ingredient ingredient)
{
// We simply check if an ingredient is present by comparing their names
return _ingredients.Any(i => i.Name == ingredient.Name);
}
}
Pay attention to the ContainsIngredient and ContainsIngredients methods here. Now you can do stuff like if (roll.ContainsIngredient(ingredient)), which will make your code more expressive and more readable. You'll see this in action in the next class that I'm going to add, RollCollection.
You are modeling collections of food to pick from, presumably in the context of a restaurant menu or some similar domain. You might as well go ahead and model just that: a RollCollection. This will allow you to encapsulate some meaningful logic inside of the collection.
Again, this sort of thing tends to require some boilerplate code and may look overly complex at first, but it will make your classes easier to consume. So let's add a RollCollection:
public class RollCollection : IEnumerable<Roll>
{
private List<Roll> _rolls = new List<Roll>();
public RollCollection()
{
// We need to provide a default constructor if we want to be able
// to instantiate an empty RollCollection and then add rolls later on
}
public RollCollection(IEnumerable<Roll> rolls)
{
// By providing a constructor overload which accepts an IEnumerable<Roll>,
// we have the opportunity to create a new RollCollection based on a filtered existing collection of rolls
_rolls = rolls.ToList();
}
public RollCollection WhichContainIngredients(IEnumerable<Ingredient> ingredients)
{
IEnumerable<Roll> filteredRolls = _rolls
.Where(r => r.ContainsIngredients(ingredients));
return new RollCollection(filteredRolls);
}
public bool AddRoll(Roll roll)
{
// Similar to AddIngredient
bool alreadyContainsRoll = _rolls.Any(r => r.Name == roll.Name);
if (!alreadyContainsRoll)
{
_rolls.Add(roll);
return true;
}
return false;
}
#region IEnumerable implementation
public IEnumerator<Roll> GetEnumerator()
{
foreach (Roll roll in _rolls)
{
yield return roll;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
WhichContainIngredients is the thing we were really looking for, as it allows you to do something like this:
// I have omitted the (proper) instantiation of Rolls and ChosenIngredients for brevity here
public RollCollection Rolls { get; set; }
public List<Ingredient> ChosenIngredients { get; set; }
public void Update()
{
Rolls = Rolls.WhichContainIngredients(ChosenIngredients);
}
This is simple and clean, just the sort of thing you want to be doing in your presentation layer. The logic to accomplish your requirement is now nicely encapsulated in the RollCollection class.
EDIT: a more complete (but still simplified) example of how your Controller class might end up looking like:
public class Controller
{
private RollCollection _availableRolls = new RollCollection();
private List<Ingredient> _availableIngredients = new List<Ingredient>();
public RollCollection AvailableRolls
{
get { return _availableRolls; }
}
public List<Ingredient> AvailableIngredients
{
get { return _availableIngredients; }
}
public RollCollection RollsFilteredByIngredients
{
get { return AvailableRolls.WhichContainIngredients(ChosenIngredients); }
}
public List<Ingredient> ChosenIngredients { get; set; }
public Controller()
{
ChosenIngredients = new List<Ingredient>();
InitializeTestData();
}
private void InitializeTestData()
{
Ingredient ingredient1 = new Ingredient("Ingredient1");
Ingredient ingredient2 = new Ingredient("Ingredient2");
Ingredient ingredient3 = new Ingredient("Ingredient3");
_availableIngredients.Add(ingredient1);
_availableIngredients.Add(ingredient2);
_availableIngredients.Add(ingredient3);
Roll roll1 = new Roll("Roll1");
roll1.AddIngredient(ingredient1);
roll1.AddIngredient(ingredient2);
Roll roll2 = new Roll("Roll2");
roll2.AddIngredient(ingredient3);
_availableRolls.AddRoll(roll1);
_availableRolls.AddRoll(roll2);
}
}
I am trying to learn C# by making a simple program that shows the user
sushi rolls given their desired ingredients. i.e. a user wants a roll
with crab, and the program will spit out a list of sushi rolls that
contain crab.
Here's my solution to the given problem:
public class Roll
{
public string Name { get; set; }
private List<string> ingredients = new List<string>();
public IList<string> Ingredients { get { return ingredients; } }
public bool Contains(string ingredient)
{
return Ingredients.Any(i => i.Equals(ingredient));
}
}
You can use the LINQ extension method .Where to filter your collection of Rolls
public class Program
{
static void Main()
{
var allRolls = new List<Roll>
{
new Roll
{
Name = "Roll 1",
Ingredients = { "IngredientA", "Crab", "IngredientC" }
},
new Roll
{
Name = "Roll 2",
Ingredients = { "IngredientB", "IngredientC" }
},
new Roll
{
Name = "Roll 3",
Ingredients = { "Crab", "IngredientA" }
}
};
var rollsWithCrab = allRolls.Where(roll => roll.Contains("Crab"));
foreach (Roll roll in rollsWithCrab)
{
Console.WriteLine(roll.Name);
}
}
}
From what I see you're trying to remove all rolls that don't contain crab from your list of rolls. A better approach is to filter out those rolls that don't contain crab (using .Where), you can then use .ToList() if you need to manipulate the whole list directly rather than iterating through the collection (fetching one item at a time).
You should read up on Delegates, Iterators, Extension Methods and LINQ to better understand what's going on under the covers.
I've written the following code to set the properties on various classes. It works, but one of my new year's rsolutions is to make as much use of LINQ as possible and obviously this code doesn't. Is there a way to rewrite it in a "pure LINQ" format, preferably without using the foreach loops? (Even better if it can be done in a single LINQ statement - substatements are fine.)
I tried playing around with join but that didn't get me anywhere, hence I'm asking for an answer to this question - preferably without an explanation, as I'd prefer to "decompile" the solution to figure out how it works. (As you can probably guess I'm currently a lot better at reading LINQ than writing it, but I intend to change that...)
public void PopulateBlueprints(IEnumerable<Blueprint> blueprints)
{
XElement items = GetItems();
// item id => name mappings
var itemsDictionary = (
from item in items
select new
{
Id = Convert.ToUInt32(item.Attribute("id").Value),
Name = item.Attribute("name").Value,
}).Distinct().ToDictionary(pair => pair.Id, pair => pair.Name);
foreach (var blueprint in blueprints)
{
foreach (var material in blueprint.Input.Keys)
{
if (itemsDictionary.ContainsKey(material.Id))
{
material.Name = itemsDictionary[material.Id];
}
else
{
Console.WriteLine("m: " + material.Id);
}
}
if (itemsDictionary.ContainsKey(blueprint.Output.Id))
{
blueprint.Output.Name = itemsDictionary[blueprint.Output.Id];
}
else
{
Console.WriteLine("b: " + blueprint.Output.Id);
}
}
}
Definition of the requisite classes follow; they are merely containers for data and I've stripped out all the bits irrelevant to my question:
public class Material
{
public uint Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public uint Id { get; set; }
public string Name { get; set; }
}
public class Blueprint
{
public IDictionary<Material, uint> Input { get; set; }
public Product Output { get; set; }
}
I don't think this is actually a good candidate for conversion to LINQ - at least not in its current form.
Yes, you have a nested foreach loop - but you're doing something else in the top-level foreach loop, so it's not the easy-to-convert form which just contains nesting.
More importantly, the body of your code is all about side-effects, whether that's writing to the console or changing the values within the objects you've found. LINQ is great when you've got a complicated query and you want to loop over that to act on each item in turn, possibly with side-effects... but your queries aren't really complicated, so you wouldn't get much benefit.
One thing you could do is give Blueprint and Product a common interface containing Id and Name. Then you could write a single method to update the products and blueprints via itemsDictionary based on a query for each:
UpdateNames(itemsDictionary, blueprints);
UpdateNames(itemsDictionary, blueprints.SelectMany(x => x.Input.Keys));
...
private static void UpdateNames<TSource>(Dictionary<string, string> idMap,
IEnumerable<TSource> source) where TSource : INameAndId
{
foreach (TSource item in source)
{
string name;
if (idMap.TryGetValue(item.Id, out name))
{
item.Name = name;
}
}
}
This is assuming you don't actually need the console output. If you do, you could always pass in the appropriate prefix and add an "else" block in the method. Note that I've used TryGetValue instead of performing two lookups on the dictionary for each iteration.
I'll be honest, I did not read your code. For me, your question answered itself when you said "code to set the properties." You should not be using LINQ to alter the state of objects / having side effects. Yes, I know that you could write extension methods that would cause that to happen, but you'd be abusing the functional paradigm poised by LINQ, and possibly creating a maintenance burden, especially for other developers who probably won't be finding any books or articles supporting your endeaver.
As you're interested in doing as much as possible with Linq, you might like to try the VS plugin ReSharper. It will identify loops (or portions of loops) that can be converted to Linq operators. It does a bunch of other helpful stuff with Linq too.
For example, loops that sum values are converted to use Sum, and loops that apply an internal filter are changed to use Where. Even string concatenation or other recursion on an object is converted to Aggregate. I've learned more about Linq from trying the changes it suggests.
Plus ReSharper is awesome for about 1000 other reasons as well :)
As others have said, you probably don't want to do it without foreach loops. The loops signify side-effects, which is the whole point of the exercise. That said, you can still LINQ it up:
var materialNames =
from blueprint in blueprints
from material in blueprint.Input.Keys
where itemsDictionary.ContainsKey(material.Id)
select new { material, name = itemsDictionary[material.Id] };
foreach (var update in materialNames)
update.material.Name = update.name;
var outputNames =
from blueprint in blueprints
where itemsDictionary.ContainsKey(blueprint.Output.Id)
select new { blueprint, name = itemsDictionary[blueprint.Output.Id] };
foreach (var update in outputNames)
update.Output.Name = update.name;
What about this
(from blueprint in blueprints
from material in blueprint.Input.Keys
where itemsDictionary.ContainsKey(material.Id)
select new { material, name = itemsDictionary[material.Id] })
.ToList()
.ForEach(rs => rs.material.Name = rs.name);
(from blueprint in blueprints
where itemsDictionary.ContainsKey(blueprint.Output.Id)
select new { blueprint, name = itemsDictionary[blueprint.Output.Id] })
.ToList()
.ForEach(rs => rs.blueprint.Output.Name = rs.name);
See if this works
var res = from blueprint in blueprints
from material in blueprint.Input.Keys
join item in items on
material.Id equals Convert.ToUInt32(item.Attribute("id").Value)
select material.Set(x=> { Name = item.Attribute("id").Value; });
You wont find set method, for that there is an extension method created.
public static class LinqExtensions
{
/// <summary>
/// Used to modify properties of an object returned from a LINQ query
/// </summary>
public static TSource Set<TSource>(this TSource input,
Action<TSource> updater)
{
updater(input);
return input;
}
}