Component is a model,and Storage is one of its definitons.Is there a way to use a parameter instead of Storage?
public IActionResult Filtered(string parameter)
{
return View(viewModel.Where(x => x.Component.Storage != "").ToList());
}
I am assuming that parameter is of type string. This is just a sample code. You can customize it to your needs.
var res = from m in viewModel // I don't know what is inside this viewModel
where !String.IsNullOrEmpty(parameter)
select m;
return View(res);
You can use reflection to get value by parameter like this
var component = new Component { Storage = "A1" };
var valueOfName = component["Name"];
Console.WriteLine(valueOfName);
public partial class Component
{
public string Storage { get; set; }
public object this[string propertyName]
{
get
{
var type = GetType();
var property = type.GetProperty(propertyName);
if (property == null) throw new Exception("Class donesn't have this property");
var value = property.GetValue(this, null);
return value;
}
private set{};
}
}
If you can modify the class you don't need the partial key word on Component class
Updated
To get the parameter from the url add it to the function
public IActionResult Filtered(string propertyName)
{
var prop = typeof(Component).GetProperty(propertyName)
return View(viewModel.Where(x => !Equals(prop.GetValue(x.Component), "")).ToList());
}
Related
I am at a complete loss here, despite looking at multiple SO posts and anything else I can think of.
My goal here is to make a really, really simple mapper. Something I can basically use as a tool in some unit tests. It doesn't need to be sophisticated or anything -- just map high-level primitive and string values of one object to another. So the basic algorithm is:
Get all properties from TFrom
Get all properties from TTo
Get all properties that are in both, matched by name.
I know this could be a bug in that they could have the same name but a different type, but let's set that aside. It's not what I'm running into here -- the properties and types match between classes.
Create an instance of TTo that we can copy to.
For each property that was mapped between the objects:
Get the value off of the from object
Convert the value to the type of the property
Set the value on the to object
The problem is that no matter what I do, and no matter what the type of the property is (int or string, for example) I get the following:
Object does not match the target type.
Here is the code I'm using:
public TTo Map<TFrom, TTo>(TFrom from)
{
if (from == null) return default;
var fromProps = GetProperties(typeof(TFrom));
var toProps = GetProperties(typeof(TTo));
// Props that can be mapped from one to the other
var propsToCopy = fromProps.Intersect(toProps, new PropertyComparer()).ToList();
var returnObject = (TTo)Activator.CreateInstance(typeof(TTo));
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(returnObject, convertedValue, null);
}
return returnObject;
}
public PropertyInfo[] GetProperties(Type objectType)
{
var allProps = objectType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
return allProps.Where(p => p.PropertyType.IsPrimitive ||
p.PropertyType == typeof(string)).ToArray();
}
private class PropertyComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
And here's an example of a way I would call it, with sample classes:
public class Foo
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
public class FooOther
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
var foo = new Foo { IntProp = 1, StringProp = "foo" };
var mappedFoo = Map<Foo, FooOther>(foo);
About the only hint I've gotten out of Visual Studio is from the watch window: if the property type is a string, the watch window reports the type of convertedValue as object. If the property type is an int, the watch window reports object {int}.
The PropertyInfo you are using is still coupled to the type the property it is representing is a member of, so you aren't able to use it to set the value of an object of another type without the error you are getting.
Here's a shortened example of the behavior:
public class A {
public string Id {get;set;}
}
public class B {
public string Id {get;set;}
}
void Main()
{
var test = new A() { Id = "Test"};
var prop = test.GetType().GetProperty("Id");
var b = (B)Activator.CreateInstance(typeof(B));
var fromValue = prop.GetValue(test);
var converted = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(b, converted, null); // Exception
}
This makes sense if you think of the PropertyInfo as a member of A. To fix this, you'll want to get a property info that's specific to your type. I can fix up my example with the following:
var propTo = typeof(B).GetProperty(prop.Name);
propTo.SetValue(b, converted, null);
Console.WriteLine(b.Id); // Output: Test
Bringing that together, if you change the contents of your foreach to the following you should be in the clear:
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
var propTo = typeof(TTO).GetProperty(prop.Name);
propTo.SetValue(returnObject, convertedValue, null);
}
I have an issue.
Say I have a Generic class which can have generic properties of other classes and can even have a list of other classes.
If i have a function like
public void Read<T>() where T: class, new()
{
// Create an instance of our generic class
var model = new T();
var properties = typeof(T).GetProperties();
// Loop through the objects properties
for(var property in properties) {
// Set our value
SetPropertyValue(property, model, "test");
}
}
private void SetPropertyValue(PropertyInfo property, object model, string value) {
// Set our property value
property.SetValue(model, value, null);
}
that would work if I had a class like this:
public class Person
{
public string Name { get; set; }
}
and I invoked the Read method like this:
Read<Person>();
But if my Person model was like this:
public class Person
{
public string Name { get; set; }
public Company Company { get; set; }
}
public class Company
{
public string Name { get; set; }
}
And I tried to invoke the Read method again, it would fail because of the property have it's own list of properties.
What would be better is if it traversed them too. Is there a way to do that?
This answer can help.
You should end with something like this:
if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String) || ... )
SetPropertyValue(property, model, "test");
else
// You will have to recode this line,
// it's just to show you the idea how you can work around
SetPropertyValue(property, model, Read.MakeGeneric(property.Type)());
You will also need to return your model variable from your Read method.
The condition is depending on what type you want to overwrite, if it's like on your example, you can change the condition to match only strings and add a check on the else to check property that are objects.
You can set the property value directly if it is a string, otherwise you can return value of a method similar to Read that takes Type as a parameter to create a model and fill its properties recursively.
public void Read<T>() where T : class, new()
{
// Create an instance of our generic class
var model = new T();
var properties = typeof(T).GetProperties();
// Loop through the objects properties
foreach(var property in properties)
{
// Set our value
SetPropertyValue(property, model, "test");
}
}
private void SetPropertyValue(PropertyInfo property, object model, string value)
{
if (property.PropertyType == typeof(string))
{
// Set our property value
property.SetValue(model, value, null);
}
else
{
var submodel = Read(property.PropertyType);
property.SetValue(model, submodel, null);
}
}
private object Read(Type type)
{
if (!IsTypeSupported(type))
{
throw new ArgumentException();
}
var model = type.GetConstructor(new Type[0]).Invoke(new object[0]);
var properties = type.GetProperties();
foreach (var property in properties)
{
SetPropertyValue(property, model, "test");
}
return model;
}
private bool IsTypeSupported(Type type)
{
return type.IsClass && type.GetConstructor(new Type[0]) != null;
}
[HttpPost]
public string Update(string name, string value) {
var property = new Models.Property {
Prop = _db.PROPERTies.SingleOrDefault(p => p.id == 3)
};
property[name] = value;
return name + " " + value;
}
I've trying to get to the "Prop" model inside the property object by passing in the key and value that I'm wanting to change.
name = "Prop.Title"
value = "Test 123
I'm getting the following error
Error 452 Cannot apply indexing with [] to an expression of type 'Namespace.Models.Property'
Is there an alternative way to burrow into the "property" object?
Models.Property
namespace Namespace.Models
{
public class Property
{
public PROPERTy Prop { get; set; }
}
}
PROPERTy
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.PROPERTIES")]
public partial class PROPERTy : INotifyPropertyChanging, INotifyPropertyChanged
{
private string _Title;
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Title", DbType="VarChar(100)")]
public string Title
{
get
{
return this._Title;
}
set
{
if ((this._Title != value))
{
this.OnTitleChanging(value);
this.SendPropertyChanging();
this._Title = value;
this.SendPropertyChanged("Title");
this.OnTitleChanged();
}
}
}
UPDATE
In order to get it working I followed given advise and indexed the model.
object o = property;
object propValue = o.GetType().GetProperty(name.Split('.')[0]).GetValue(o, null);
propValue.GetType().GetProperty(name.Split('.')[1]).SetValue(propValue, Server.HtmlDecode(value), null);
What you're trying to use is an indexer, but your class doesn't implement that "pattern".
The pattern is relatively simple to implement. At a minimum, add this:
public object this[string key]
{
get
{
var pi = this.GetType().GetProperty(key); // find the property that matches the key
return pi.GetValue(this, null);
}
set
{
var pi = this.GetType().GetProperty(key); // find the property that matches the key
pi.SetValue(this, value, null);
}
}
It's up to you to make sure that the class actually contains a property matching the key.
This is a simple example on how I update a value in the database:
var context = new dbEntities();
var car = context.CarTable.Where(p => p.id == id).FirstOrDefault();
car.Make = "Volvo";
context.SaveChanges();
However, what I need to do now is to get the property by name instead. So this is what I in theory would like to do:
var context = new dbEntities();
var car = context.CarTable.Where(p => p.id == id).FirstOrDefault();
**car["Make"] = "Volvo";**
context.SaveChanges();
Is this possible in EF?
I wouldn't use reflection since that would be slow.
You can use expression trees, especially if you cache the expressions. Check this link for an article about it. I would write a wrapper around the code in the article which takes an object and a propertyname (string), creates/caches the func using the code in the article (or retrieves it from the cache), and executes the func.
The main question is really why do you need this?
The best way is still car.Make = "Volvo";.
If string-name is strongly needed, you can use Reflection:
var property = typeof (Car).GetProperty("Make");
property.SetValue(car, "BMW", null);
Here are 2 drawbacks:
Slow.
Compiler cannot check the string.
The other way - you can use indexer and switch:
public class Car
{
public string Make { get; set; }
public string this[String name]
{
set
{
switch (name)
{
case "Make":
Make = value;
break;
...
}
}
}
}
And then just car["Make"] = "Volvo";
It's faster, but a typ-problem occurs: you have to parse strings or operate with objects.
public class Car
{
public string Make { get; set; }
public object this[string name]
{
get
{
var property = this.GetType().GetProperties().FirstOrDefault(p => p.Name.Equals(name));
if (property != null)
{
return property.GetValue(this, null);
}
return null;
}
set
{
var property = this.GetType().GetProperties().FirstOrDefault(p => p.Name.Equals(name));
if (property != null)
{
property.SetValue(this, value, null);
}
}
}
}
is there a way to get the value of a property of a object based on its name?
For example if I have:
public class Car : Vehicle
{
public string Make { get; set; }
}
and
var car = new Car { Make="Ford" };
I want to write a method where I can pass in the property name and it would return the property value. ie:
public string GetPropertyValue(string propertyName)
{
return the value of the property;
}
return car.GetType().GetProperty(propertyName).GetValue(car, null);
You'd have to use reflection
public object GetPropertyValue(object car, string propertyName)
{
return car.GetType().GetProperties()
.Single(pi => pi.Name == propertyName)
.GetValue(car, null);
}
If you want to be really fancy, you could make it an extension method:
public static object GetPropertyValue(this object car, string propertyName)
{
return car.GetType().GetProperties()
.Single(pi => pi.Name == propertyName)
.GetValue(car, null);
}
And then:
string makeValue = (string)car.GetPropertyValue("Make");
You want Reflection
Type t = typeof(Car);
PropertyInfo prop = t.GetProperty("Make");
if(null != prop)
return prop.GetValue(this, null);
Expanding on Adam Rackis's answer - we can make the extension method generic simply like this:
public static TResult GetPropertyValue<TResult>(this object t, string propertyName)
{
object val = t.GetType().GetProperties().Single(pi => pi.Name == propertyName).GetValue(t, null);
return (TResult)val;
}
You can throw some error handling around that too if you like.
In addition other guys answer, its Easy to get property value of any object by use Extension method like:
public static class Helper
{
public static object GetPropertyValue(this object T, string PropName)
{
return T.GetType().GetProperty(PropName) == null ? null : T.GetType().GetProperty(PropName).GetValue(T, null);
}
}
Usage is:
Car foo = new Car();
var balbal = foo.GetPropertyValue("Make");
Simple sample (without write reflection hard code in the client)
class Customer
{
public string CustomerName { get; set; }
public string Address { get; set; }
// approach here
public string GetPropertyValue(string propertyName)
{
try
{
return this.GetType().GetProperty(propertyName).GetValue(this, null) as string;
}
catch { return null; }
}
}
//use sample
static void Main(string[] args)
{
var customer = new Customer { CustomerName = "Harvey Triana", Address = "Something..." };
Console.WriteLine(customer.GetPropertyValue("CustomerName"));
}
To avoid reflection you could set up a Dictionary with your propery names as keys and functions in the dictionary value part that return the corresponding values from the properties that you request.
2 Very short options, 1 with a default value if it fails:
public object GetPropertyValue_WithDefault(
object _t,
string _prop,
object _default = null
)
{
PropertyInfo pi = _t.GetType().GetProperty(_prop);
return (pi == null
? _default
: pi.GetValue(_t, null)
);
}
public object GetPropertyValue(object _t, string _prop)
{
//because of "?." will return null if property not found
return _t.GetType().GetProperty(_prop)?.GetValue(_t, null);
}