Dynamically Insert A Property Into A LINQ Query Statement
-
Suppose I have 2 objects like the following
public class Toy
{
public int ID{get;set;}
public double Price{get;set;}
public sting Color{get; set;}
}
public class Merchandise
{
public List toyList;
public Merchandise()
{
toyList= new List();
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
}public string this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}
}I want to be able to dynamically choose which property to insert into my LINQ query inside of the Indexer's set property. That way I can set the Price or Color of the Toy object dynamically. I created the toyProperty parameter in the Indexer parameter list so that the code that will consume the Indexer will be able to pass a value to the toyProperty in the LINQ Query. The code consuming the Indexer will look something like the following
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
//Change Price Dynamically
item[2, "Price"].Price = price;//Change Color Dynamically
item[2, "Color"].Color = color;My Lambda Expression inside the set property of the Indexer does not work of course but I wonder if it is missing something that could help to make it work.
-
Suppose I have 2 objects like the following
public class Toy
{
public int ID{get;set;}
public double Price{get;set;}
public sting Color{get; set;}
}
public class Merchandise
{
public List toyList;
public Merchandise()
{
toyList= new List();
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
}public string this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}
}I want to be able to dynamically choose which property to insert into my LINQ query inside of the Indexer's set property. That way I can set the Price or Color of the Toy object dynamically. I created the toyProperty parameter in the Indexer parameter list so that the code that will consume the Indexer will be able to pass a value to the toyProperty in the LINQ Query. The code consuming the Indexer will look something like the following
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
//Change Price Dynamically
item[2, "Price"].Price = price;//Change Color Dynamically
item[2, "Color"].Color = color;My Lambda Expression inside the set property of the Indexer does not work of course but I wonder if it is missing something that could help to make it work.
While there would be a way to do something close to what you want (which I wouldn't recommend), I don't think you've thought this through: The indexer-getter "attempts" to return a
Toy
but you declared the type of the indexer asstring
. Maybe you intended to dynamically access the giventoyProperty
there as well and return it formatted as astring
? Your usage-example actually employs the getter, not the setter, because you (statically) access a property before the assignment. Apart from that, you're planning on conceding syntax correctness and type safety here. Why? If you tell us your ultimate goal here - why you think you need this - we will probably be able to provide you with ideas for better solutions.If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
-
While there would be a way to do something close to what you want (which I wouldn't recommend), I don't think you've thought this through: The indexer-getter "attempts" to return a
Toy
but you declared the type of the indexer asstring
. Maybe you intended to dynamically access the giventoyProperty
there as well and return it formatted as astring
? Your usage-example actually employs the getter, not the setter, because you (statically) access a property before the assignment. Apart from that, you're planning on conceding syntax correctness and type safety here. Why? If you tell us your ultimate goal here - why you think you need this - we will probably be able to provide you with ideas for better solutions.If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
Thanks for your reply. In my test project my code does return a Toy object. It looks something like this
public Toy this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}This is just a test project to find out how I can use a single set property to change different properties using one LINQ Query. Also you are correct my syntax should have been
//Change Price Dynamically
item[2, "Price"] = price;//Change Color Dynamically
item[2, "Color"] = color; -
Thanks for your reply. In my test project my code does return a Toy object. It looks something like this
public Toy this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}This is just a test project to find out how I can use a single set property to change different properties using one LINQ Query. Also you are correct my syntax should have been
//Change Price Dynamically
item[2, "Price"] = price;//Change Color Dynamically
item[2, "Color"] = color;MadDashCoder wrote:
In my test project my code does return a Toy object.
Then the
value
in the setter would also be of typeToy
. The only half sensible types to use would beobject
orstring
.MadDashCoder wrote:
This is just a test project to find out how I can use a single set property to change different properties using one LINQ Query.
It's always going to be messy and I would avoid it unless there's some very compelling reason to do so. Staying with your pseudo-code-style, it should look like this:
public object this[int toyId, string toyProperty]
{
get { return listOfProducts.Single(prod => prod.Id == toyId).toyProperty; }
set { listOfProducts.Single(prod => prod.Id == toyId).toyProperty = value; }
}And to resolve the
.toyProperty
pseudo-code you would have to use reflection or reflection+expressions.If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
-
Suppose I have 2 objects like the following
public class Toy
{
public int ID{get;set;}
public double Price{get;set;}
public sting Color{get; set;}
}
public class Merchandise
{
public List toyList;
public Merchandise()
{
toyList= new List();
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
}public string this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}
}I want to be able to dynamically choose which property to insert into my LINQ query inside of the Indexer's set property. That way I can set the Price or Color of the Toy object dynamically. I created the toyProperty parameter in the Indexer parameter list so that the code that will consume the Indexer will be able to pass a value to the toyProperty in the LINQ Query. The code consuming the Indexer will look something like the following
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
//Change Price Dynamically
item[2, "Price"].Price = price;//Change Color Dynamically
item[2, "Color"].Color = color;My Lambda Expression inside the set property of the Indexer does not work of course but I wonder if it is missing something that could help to make it work.
After thinking about this for a couple of minutes, the answer is "probably yes", but the code ends up being hideously complex because of other property types outside of the string case you're showing. You have to handle all possible types through reflection, even structures and class instances. This isn't something I've done, nor want to because of time.
A guide to posting questions on CodeProject
Click this: Asking questions is a skill. Seriously, do it.
Dave Kreskowiak -
Suppose I have 2 objects like the following
public class Toy
{
public int ID{get;set;}
public double Price{get;set;}
public sting Color{get; set;}
}
public class Merchandise
{
public List toyList;
public Merchandise()
{
toyList= new List();
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
}public string this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}
}I want to be able to dynamically choose which property to insert into my LINQ query inside of the Indexer's set property. That way I can set the Price or Color of the Toy object dynamically. I created the toyProperty parameter in the Indexer parameter list so that the code that will consume the Indexer will be able to pass a value to the toyProperty in the LINQ Query. The code consuming the Indexer will look something like the following
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
//Change Price Dynamically
item[2, "Price"].Price = price;//Change Color Dynamically
item[2, "Color"].Color = color;My Lambda Expression inside the set property of the Indexer does not work of course but I wonder if it is missing something that could help to make it work.
As the others have said, this is probably possible but almost certainly not advisable! You would lose type safety and compile-time checking for correct property identification, etc. Here are my initial thoughts on how I'd do this.
public class Toy
{
// changing the ID is probably NOT a good idea.
// put it onto the constructor
public Toy(int id)
{
ID = id;
}
public int ID { get; private set; }
// unless you need ID to be a property (like for data binding)
// this might be a case for a public readonly field:
// public readonly int ID;
public double Price { get; set; }
public string Color { get; set; }
}public class Merchandise
{
public Dictionary<int, Toy> toys;
public Merchandise()
{
var toyList = new List<Toy>();
// presumably you're actually loading the real data from some other source...
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
toys = toyList.ToDictionary(t => t.ID);
}public string this[int toyId]
{
get
{
Toy theToy;
toys.TryGetValue(toyID, out theToy); // In this case we can ignore the return value
return theToy;
}
}
}Then the usage is:
// this is CLEARLY going to fail!
// You need to use double.TryParse(txtPrice.Text, ...) with appropriate error handling
// to convert it from a string to a double!!!!!
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
// Before you reference the .Price or .Color properties,
// you need to validate that item[2] didn't return null!!!// What you had:
//Change Price Dynamically
// item[2, "Price"].Price = price;
// this ^^^^^^^^^ was pointless since passing the property name to the indexer didn't accomplish anything
item[2].Price = price; // with my indexer definition//Change Color Dynamically
// item[2, "Color"].Color = color;
// again ^^^^^^^^^ pointless
item[2].Color = color;// Instead, more like:
Toy theToy = item[2];
if (theToy != null)
{
//Change Price Dynamically
theToy.Price = price;
//Change Color Dynamically
theToy.Color = color;
}
else
{
// report the no such toy with that ID error !!
}"Fairy tales do not tell children the dragon
-
As the others have said, this is probably possible but almost certainly not advisable! You would lose type safety and compile-time checking for correct property identification, etc. Here are my initial thoughts on how I'd do this.
public class Toy
{
// changing the ID is probably NOT a good idea.
// put it onto the constructor
public Toy(int id)
{
ID = id;
}
public int ID { get; private set; }
// unless you need ID to be a property (like for data binding)
// this might be a case for a public readonly field:
// public readonly int ID;
public double Price { get; set; }
public string Color { get; set; }
}public class Merchandise
{
public Dictionary<int, Toy> toys;
public Merchandise()
{
var toyList = new List<Toy>();
// presumably you're actually loading the real data from some other source...
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
toys = toyList.ToDictionary(t => t.ID);
}public string this[int toyId]
{
get
{
Toy theToy;
toys.TryGetValue(toyID, out theToy); // In this case we can ignore the return value
return theToy;
}
}
}Then the usage is:
// this is CLEARLY going to fail!
// You need to use double.TryParse(txtPrice.Text, ...) with appropriate error handling
// to convert it from a string to a double!!!!!
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
// Before you reference the .Price or .Color properties,
// you need to validate that item[2] didn't return null!!!// What you had:
//Change Price Dynamically
// item[2, "Price"].Price = price;
// this ^^^^^^^^^ was pointless since passing the property name to the indexer didn't accomplish anything
item[2].Price = price; // with my indexer definition//Change Color Dynamically
// item[2, "Color"].Color = color;
// again ^^^^^^^^^ pointless
item[2].Color = color;// Instead, more like:
Toy theToy = item[2];
if (theToy != null)
{
//Change Price Dynamically
theToy.Price = price;
//Change Color Dynamically
theToy.Color = color;
}
else
{
// report the no such toy with that ID error !!
}"Fairy tales do not tell children the dragon
Thank all for your valuable inputs especially you Matt. The purpose of this project was just to explore how to dynamically choose a property to be inserted into a LINQ query at runtime. I've learned a lot from everyone's input. I was able to do what I had originally set out to do using the code below although it is not as elegant as your solution.
public string this[int toyId, string price, string color]
{
get
{
string result = string.Empty;
if (color != null)
{
result = toyList.FirstOrDefault(toy => toy.ID == toyId).Color;
}
else if (price != null)
{
result = (toyList.FirstOrDefault(toy => toy.ID == toyId).Price).ToString();
}
return result;
}
set
{
if (color != null)
{
toyList.FirstOrDefault(toy => toy.ID == toyId).Color = value;
}
else if (price != null)
{
toyList.FirstOrDefault(toy => toy.ID == toyId).Price = Convert.ToDouble(value);
}
}
} -
Thank all for your valuable inputs especially you Matt. The purpose of this project was just to explore how to dynamically choose a property to be inserted into a LINQ query at runtime. I've learned a lot from everyone's input. I was able to do what I had originally set out to do using the code below although it is not as elegant as your solution.
public string this[int toyId, string price, string color]
{
get
{
string result = string.Empty;
if (color != null)
{
result = toyList.FirstOrDefault(toy => toy.ID == toyId).Color;
}
else if (price != null)
{
result = (toyList.FirstOrDefault(toy => toy.ID == toyId).Price).ToString();
}
return result;
}
set
{
if (color != null)
{
toyList.FirstOrDefault(toy => toy.ID == toyId).Color = value;
}
else if (price != null)
{
toyList.FirstOrDefault(toy => toy.ID == toyId).Price = Convert.ToDouble(value);
}
}
}This still suffers from the problems that I mentioned. It isn't even close to type-safe. You'll get a
NullReferenceException
if thetoyId
isn't found, unless bothprice
andcolor
arenull
, in which case you'll never know! For the "price" case, if the string is not a validdouble
, it willthrow
aFormatException
(or, less likely,OverflowException
). This is the wrong place in a design to be parsing the price value string todouble
! It ought to be done as near as possible to where the user provides the input so it can be reported appropriately. Finally, in fact, you did not "dynamically choose a property to be inserted into a LINQ query at runtime". You're choosing different properties of the result of identical LINQ queries based on boolean conditions represented by string values beingnull
or not. I would consider this abuse of the indexer to gain a syntactic shortcut of very dubious value. So lesson learned. Don't do this in production code! (Or any code! ;) ) This is serious "code smell". I'm sorry, but it needed to be said. X|"Fairy tales do not tell children the dragons exist. Children already know that dragons exist. Fairy tales tell children the dragons can be killed." - G.K. Chesterton
-
Suppose I have 2 objects like the following
public class Toy
{
public int ID{get;set;}
public double Price{get;set;}
public sting Color{get; set;}
}
public class Merchandise
{
public List toyList;
public Merchandise()
{
toyList= new List();
toyList.Add(new Toy {ID = 1, Price = 10, Color = "Red"});
toyList.Add(new Toy {ID = 2, Price = 10, Color = "White"});
toyList.Add(new Toy {ID = 3, Price = 10, Color = "Black"});
}public string this[int toyId, string toyProperty]
{
get{return listOfProducts.FirstOrDefault(prod => prod.Id == productId);}
set{listOfProducts.FirstOrDefault(prod => prod.Id == productId).toyProperty = value;}
}
}I want to be able to dynamically choose which property to insert into my LINQ query inside of the Indexer's set property. That way I can set the Price or Color of the Toy object dynamically. I created the toyProperty parameter in the Indexer parameter list so that the code that will consume the Indexer will be able to pass a value to the toyProperty in the LINQ Query. The code consuming the Indexer will look something like the following
public double price = txtPrice.Text;
public string color = txtColor.Text;public Merchandise item = new Merchandise();
//Change Price Dynamically
item[2, "Price"].Price = price;//Change Color Dynamically
item[2, "Color"].Color = color;My Lambda Expression inside the set property of the Indexer does not work of course but I wonder if it is missing something that could help to make it work.
Restated another way, the question is how to update an existing item in a collection using LINQ:
List toys = new List(); toys.Add( new Toy() { Id = 1, Color = "Red", Price = 1.20M } ); toys.Add( new Toy() { Id = 2, Color = "Red", Price = 1.20M } ); toys.Add( new Toy() { Id = 3, Color = "Red", Price = 1.20M } ); toys.Single( o => o.Id == 2 ).Color = "Blue"; toys.Single( o => o.Id == 2 ).Price = 3.99M; toys.ForEach( o => Console.WriteLine( $"{o.Id} {o.Color} {o.Price}" ) );