How to access properties by name? - c#

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);
}
}
}
}

Related

Using parameter instead of a model definiton

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());
}

.Net accessing instantiated class members by string variable [duplicate]

I have an instance of the Account class. Each account object has an owner, reference, etc.
One way I can access an accounts properties is through accessors like
account.Reference;
but I would like to be able to access it using dynamic string selectors like:
account["PropertyName"];
just like in JavaScript. So I would have account["Reference"] which would return the value, but I also would like to be able to assign a new value after that like:
account["Reference"] = "124ds4EE2s";
I've noticed I can use
DataBinder.Eval(account,"Reference")
to get a property based on a string, but using this I can't assign a value to the property.
Any idea on how I could do that?
First of all, you should avoid using this; C# is a strongly-typed language, so take advantage of the type safety and performance advantages that accompany that aspect.
If you have a legitimate reason to get and set the value of a property dynamically (in other words, when the type and/or property name is not able to be defined in the code), then you'll have to use reflection.
The most inline-looking way would be this:
object value = typeof(YourType).GetProperty("PropertyName").GetValue(yourInstance);
...
typeof(YourType).GetProperty("PropertyName").SetValue(yourInstance, "value");
However, you can cache the PropertyInfo object to make it more readable:
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");
object value = prop.GetValue(yourInstance);
...
prop.SetValue(yourInstance, "value");
You could try combining the indexer with reflection...
public object this[string propertyName]
{
get
{
PropertyInfo property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
set
{
PropertyInfo property = GetType().GetProperty(propertyName);
property.SetValue(this,value, null);
}
}
If they are your own objects you could provide an indexer to access the fields. I don't really recommend this but it would allow what you want.
public object this[string propertyName]
{
get
{
if(propertyName == "Reference")
return this.Reference;
else
return null;
}
set
{
if(propertyName == "Reference")
this.Reference = value;
else
// do error case here
}
}
Note that you lose type safety when doing this.
I used the reflection method from Richard, but elaborated the set method to handle other types being used such as strings and nulls.
public object this[string propertyName]
{
get
{
PropertyInfo property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
set
{
PropertyInfo property = GetType().GetProperty(propertyName);
Type propType = property.PropertyType;
if (value == null)
{
if (propType.IsValueType && Nullable.GetUnderlyingType(propType) == null)
{
throw new InvalidCastException();
}
else
{
property.SetValue(this, null, null);
}
}
else if (value.GetType() == propType)
{
property.SetValue(this, value, null);
}
else
{
TypeConverter typeConverter = TypeDescriptor.GetConverter(propType);
object propValue = typeConverter.ConvertFromString(value.ToString());
property.SetValue(this, propValue, null);
}
}
}
The SetValue() function will throw an error if the conversion doesn't work.
If you are using .Net 4 you can use the dynamic keyword now.
dynamic foo = account;
foo.Reference = "124ds4EE2s";
I agree with the previous posters that you probably do need to be using the properties. Reflection is very slow compared to direct property access.
On the other hand, if you need to maintain a list of user-defined properties, then you can't use C# properties. You need to pretend you are a Dictionary, or you need to expose a property that behaves like a Dictionary. Here is an example of how you could make the Account class support user-defined properties:
public class Account
{
Dictionary<string, object> properties;
public object this[string propertyName]
{
get
{
if (properties.ContainsKey[propertyName])
return properties[propertyName];
else
return null;
}
set
{
properties[propertyName] = value;
}
}
}
I personally prefer to work with extension methods so here is my code :
public static class ReflectionExtensions
{
public static void SetPropertyValue(this object Target,
string PropertyName,
object NewValue)
{
if (Target == null) return; //or throw exception
System.Reflection.PropertyInfo prop = Target.GetType().GetProperty(PropertyName);
if (prop == null) return; //or throw exception
object value = prop.GetValue(Target, null);
prop.SetValue(Target, NewValue, null);
}
}
You need to use Reflection:
PropertyInfo property = typeof(Account).GetProperty("Reference");
property.SetValue(myAccount, "...", null);
Note that this will be very slow.
Use reflection and expression bodies
public dynamic this[string memberName]
{
get => GetType().GetProperty(memberName).GetValue(this, null);
set => GetType().GetProperty(memberName).SetValue(this,value, null);
}
how to access the list in an object using reflection by string name
public List Table1 { get; set; } = new List();
Here is a simple example, I hope it helps
static void Main(string[] args)
{
Operators op = new Operators()
{
ID = 1,
Name = "Edward",
email = "e.lorenz#mail.com",
Pass = "123456",
Auth1 = "EDF3242523#FFSDGDF"
};
var typei = op.GetType();
var ss = typei.GetProperties().Where(m => m.GetCustomAttributes<Password>().Count() > 0);
foreach (var item in ss)
{
var text = typei.GetProperty(item.Name).GetValue(op).ToString();
typei.GetProperty(item.Name).SetValue(op, Encrypt(text));
}
Console.WriteLine(op.Pass);
Console.WriteLine(op.Auth1);
Console.ReadKey();
}

Document Update Using Driver Ignoring _id

I'd like to be able to update a key/value pair within a Document in a Collection without regard to the _id. This SO response appears to provide a way to do what I want, but it requires a focus on the _id key.
Essentially, what can I do to get my code, as shown below, to work without adding the [BsonIgnoreExtraElements] attribute to the class?
Based on the code which I'll show below, I think I'm close, and I do have a possible workaround, using the attribute, which I'll also show. However, I'd like to do this without decorating the classes with any attributes if possible.
Why no _id? Personally, I find the _id field just gets in the way. It's not part of any of my classes, and MongoDB isn't relational, so I simply don't use it. I freely admit that I may be completely missing the intent and idea behind the use of the _id field, but I simply don't see a need for it in MongoDB. Nevertheless, if possible, I'd like to focus on working without a focus on the _id field.
With that said, I have methods for retrieving a single document or an entire collection along with inserting a single document or entire collection, generically, without regard to the _id field.
I found that the key to ignoring the _id in a "select" is to use a Projection. I'm not all that familiar with the driver, but here are the lines of code that do the magic:
public const string ELEMENT_ID = "_id";
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
For background info, here is a retrieval method, ignoring _id:
public T GetSingle<T>(string property, string value) where T : class, new()
{
T tObject = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
string className = typeof(T).ToString();
int lastPeriod = className.LastIndexOf('.');
int length = className.Length - lastPeriod;
className = className.Substring(lastPeriod + 1, length - 1);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(value);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
tObject = found.FirstOrDefault<T>();
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return tObject;
}
Similar to other another SO response, I've tried to use FindOneAndUpdate, but I receive the following error:
Element '_id' does not match any field or property of class
ClassThingy.Suffix.
If I could apply the Projection to the FindOneAndUpdate somehow, I think that might resolve my issue, but I'm not able to find a way to do that application.
Here is my code:
public T UpdateSingle<T>(T item, string property, object originalValue, object newValue) where T : class, new()
{
string className = string.Empty;
T updatedDocument = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
className = ClassUtility.GetClassNameFromObject<T>(item);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(originalValue);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition);
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return updatedDocument;
}
Interestingly enough, while the FindOneAndUpdate method actually appears to succeed and the "Suffix" collection does, in fact, get modified. Also, the return doesn't contain the modification. Instead, it's the "original" (when I use the workaround as shown below).
More Info:
Suffix Class:
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
Suffix suffix = new Suffix();
MongoRepository.MongoRepository mongoRepository = new MongoRepository.MongoRepository("MyDataBase");
mongoRepository.UpdateSingle<Suffix>(suffix, "Description", "Jr", "Junior");
Workaround:
[BsonIgnoreExtraElements]
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
But, much rather not use the attribute if at all possible.
One thing you're missing here is the third parameter of .FindOneAndUpdate() method which is of type FindOneAndUpdateOptions<T,T>. That's the place where you can define if you want to get document After or Before the modification. Before is the default value Moreover you can specify the projection and exclude _id property. Try:
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
ProjectionDefinition<T, T> projection = Builders<T>.Projection.Exclude("_id");
var options = new FindOneAndUpdateOptions<T>()
{
Projection = projection,
ReturnDocument = ReturnDocument.After
};
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition, options);

How to check all properties of an object whether null or empty?

I have an object lets call it ObjectA
and that object has 10 properties and those are all strings.
var myObject = new {Property1="",Property2="",Property3="",Property4="",...}
is there anyway to check to see whether all these properties are null or empty?
So any built-in method that would return true or false?
If any single of them is not null or empty then the return would be false. If all of them are empty it should return true.
The idea is I do not want to write 10 if statement to control if those properties are empty or null.
Thanks
You can do it using Reflection
bool IsAnyNullOrEmpty(object myObject)
{
foreach(PropertyInfo pi in myObject.GetType().GetProperties())
{
if(pi.PropertyType == typeof(string))
{
string value = (string)pi.GetValue(myObject);
if(string.IsNullOrEmpty(value))
{
return true;
}
}
}
return false;
}
Matthew Watson suggested an alternative using LINQ:
return myObject.GetType().GetProperties()
.Where(pi => pi.PropertyType == typeof(string))
.Select(pi => (string)pi.GetValue(myObject))
.Any(value => string.IsNullOrEmpty(value));
I suppose you want to make sure that all properties are filled in.
A better option is probably by putting this validation in the constructor of your class and throw exceptions if validation fails. That way you cannot create a class that is invalid; catch exceptions and handle them accordingly.
Fluent validation is a nice framework (http://fluentvalidation.codeplex.com) for doing the validation. Example:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Property1).NotNull();
RuleFor(customer => customer.Property2).NotNull();
RuleFor(customer => customer.Property3).NotNull();
}
}
public class Customer
{
public Customer(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
new CustomerValidator().ValidateAndThrow();
}
public string Property1 {get; set;}
public string Property2 {get; set;}
public string Property3 {get; set;}
}
Usage:
try
{
var customer = new Customer("string1", "string", null);
// logic here
} catch (ValidationException ex)
{
// A validation error occured
}
PS - Using reflection for this kind of thing just makes your code harder to read. Using validation as shown above makes it explicitly clear what your rules are; and you can easily extend them with other rules.
The following code returns if any property is not null.
return myObject.GetType()
.GetProperties() //get all properties on object
.Select(pi => pi.GetValue(myObject)) //get value for the property
.Any(value => value != null); // Check if one of the values is not null, if so it returns true.
Here you go
var instOfA = new ObjectA();
bool isAnyPropEmpty = instOfA.GetType().GetProperties()
.Where(p => p.GetValue(instOfA) is string) // selecting only string props
.Any(p => string.IsNullOrWhiteSpace((p.GetValue(instOfA) as string)));
and here's the class
class ObjectA
{
public string A { get; set; }
public string B { get; set; }
}
A slightly different way of expressing the linq to see if all string properties of an object are non null and non empty:
public static bool AllStringPropertyValuesAreNonEmpty(object myObject)
{
var allStringPropertyValues =
from property in myObject.GetType().GetProperties()
where property.PropertyType == typeof(string) && property.CanRead
select (string) property.GetValue(myObject);
return allStringPropertyValues.All(value => !string.IsNullOrEmpty(value));
}
Note if you've got a data structural hierarchy and you want to test everything in that hierarchy, then you can use a recursive method. Here's a quick example:
static bool AnyNullOrEmpty(object obj) {
return obj == null
|| obj.ToString() == ""
|| obj.GetType().GetProperties().Any(prop => AnyNullOrEmpty(prop.GetValue(obj)));
}
To only check if all properties are null:
bool allPropertiesNull = !myObject.GetType().GetProperties().Any(prop => prop == null);
you can use reflection and extension methods to do this.
using System.Reflection;
public static class ExtensionMethods
{
public static bool StringPropertiesEmpty(this object value)
{
foreach (PropertyInfo objProp in value.GetType().GetProperties())
{
if (objProp.CanRead)
{
object val = objProp.GetValue(value, null);
if (val.GetType() == typeof(string))
{
if (val == "" || val == null)
{
return true;
}
}
}
}
return false;
}
}
then use it on any object with string properties
test obj = new test();
if (obj.StringPropertiesEmpty() == true)
{
// some of these string properties are empty or null
}
No, I don't think there is a method to do exactly that.
You'd be best writing a simple method that takes your object and returns true or false.
Alternatively, if the properties are all the same, and you just want to parse through them and find a single null or empty, perhaps some sort of collection of strings would work for you?
You can try the following query :
if the object is "referenceKey" (where few properties may be null )
referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) == null)
I need to count the properties where the value is set to not Null, so I have used the following query :
var countProvidedReferenceKeys = referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) != null).Count();

Union of Two Objects Based on Equality of Their Members

Let us start with a class definition for the sake of example:
public class Person
{
public string FirstName;
public string LastName;
public int Age;
public int Grade;
}
Now let's assume I have a List<Person> called people containing 3 objects:
{"Robby", "Goki", 12, 8}
{"Bobby", "Goki", 10, 8}
{"Sobby", "Goki", 10, 8}
What I am looking for is some way to retrieve the following single Person object:
{null, "Goki", -1, 8}
where fields which are the same in all objects retain their value while fields which have multiple values are replaced with some invalid value.
My first thought consisted of:
Person unionMan = new Person();
if (people.Select(p => p.FirstName).Distinct().Count() == 1)
unionMan.FirstName = people[0].FirstName;
if (people.Select(p => p.LastName).Distinct().Count() == 1)
unionMan.LastName = people[0].LastName;
if (people.Select(p => p.Age).Distinct().Count() == 1)
unionMan.Age = people[0].Age;
if (people.Select(p => p.Grade).Distinct().Count() == 1)
unionMan.Grade = people[0].Grade;
Unfortunately, the real business object has many more members than four and this is both tedious to write and overwhelming for someone else to see for the first time.
I also considered somehow making use of reflection to put these repetitive checks and assignments in a loop:
string[] members = new string[] { "FirstName", "LastName", "Age", "Grade" };
foreach (string member in members)
{
if (people.Select(p => p.**member**).Distinct().Count() == 1)
unionMan.**member** = people[0].**member**;
}
where **member** would be however reflection would allow the retrieval and storage of that particular member (assuming it is possible).
While the first solution would work, and the second I am assuming would work, does anyone have a better alternative solution to this problem? If not, would using reflection as described above be feasible?
It's inefficient to do a distinct of all the values just to count the distinct members. You have a shortcut scenario wherein finding one value in any of the subsequent items that does not have the same value as the first item's member means that you have an invalid state for that column.
Something like this should work, though more work would need to be done if any of the members are arrays, need recursive evaluation or other more complex logic (note I have not tested this):
public static T UnionCombine<T>(this IEnumerable<T> values) where T : new() {
var newItem = new T();
var properties = typeof(T).GetProperties();
for (var prop in properties) {
var pValueFirst = prop.GetValue(values.First(), null);
var useDefaultValue = values.Skip(1).Any(v=>!(Object.Equals(pValueFirst, prop.GetValue(v, null))));
if (!useDefaultValue) prop.SetValue(newItem, pValueFirst, null);
}
return newItem;
}
Your last idea seems good to me, something like this:
List<Person> persons = new List<Person>()
{
new Person(){ FirstName="Robby", LastName="Goki", Age=12, Grade=8},
new Person(){ FirstName="Bobby", LastName="Goki", Age=10, Grade=8},
new Person(){ FirstName="Sobby", LastName="Goki", Age=10, Grade=8},
};
var properties = typeof(Person).GetProperties();
var unionMan = new Person();
foreach (var propertyInfo in properties)
{
var values = persons.Select(x => propertyInfo.GetValue(x, null)).Distinct();
if (values.Count() == 1)
propertyInfo.SetValue(unionMan, propertyInfo.GetValue(persons.First(), null), null);
}
A couple of observations:
your class members should be defined as properties and not public members
and both get and set accessor must be public
the default constructor should define the "invalid" values (as correctly suggested by #RaphaƫlAlthaus)
so, the class Person would look like this:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int Grade { get; set; }
public Person()
{
this.FirstName = null;
this.LastName = null;
this.Age = -1;
this.Grade = -1;
}
}
Update: Since you don't have control over the Person class, and state is defined in public fields, rather than properties, I have updated the solution to address this.
I would recommend using reflection. You would want to get the FieldInfo (or PropertyInfo) object ahead of time, rather than getting it for each entry in in your LINQ query. You can get them by using Type.GetField and Type.GetProperty. Once you have those, you can simply use FieldInfo/PropertyInfo.GetValue and FieldInfo/PropertyInfo.SetValue.
For example:
Type personType = typeof(Person);
foreach(string member in members)
{ // Get Fields via Reflection
FieldInfo field = peopleType.GetField(member);
if(field != null)
{
if (people.Select(p => field.GetValue(p, null) ).Distinct().Count() == 1)
{
field.SetValue(unionMan, field.GetValue(people[0], null), null);
}
}
else // If member is not a field, check if it's a property instead
{ // Get Properties via Reflection
PropertyInfo prop = peopleType.GetProperty(member);
if(prop != null)
{
if (people.Select(p => prop.GetValue(p, null) ).Distinct().Count() == 1)
{
prop.SetValue(unionMan, prop.GetValue(people[0], null), null);
}
}
}
}
As you pointed out, you are already setting the "invalid" vlaues in the default constructor, so you shouldn't have to worry about them inside this loop.
Note: In my example, I used the versions of GetField and GetProperties that do not take a BindingFlags parameter. These will only return public members.

Categories

Resources