I need to pass selection of properties of some types(one type each time), assume this is my type:
public class Product {
[PrimaryKey]
public long Id { get; set; }
[DisplayName("Name")]
public string Title { get; set; }
[Foreignkey(Schema = "Products", Table = "MajorCategory", Column = "Id")]
[DisplayName("MCat")]
public string MajorCategory { get; set; }
[Foreignkey(Schema = "Products", Table = "Category", Column = "Id")]
[DisplayName("Cat")]
public string Category { get; set; }
public long CategoryId { get; set; }
[BoolAsRadio()]
public bool IsScanAllowed { get; set; }
}
So I need a way to pass the list of properties of this type to other Type(Target Type), and use property name, and attributes, and I don't need values, something like the following Pseudo-code:
List<Property> propertyList = new List<Property>();
propertyList.Add(Product.Id);
PropertyList.Add(Product.Title);
TargetType target = new TargetType();
target.Properties = propertyList;
public class TargetType {
public List<Property> Properties { get; set;}
GetAttributes() {
foreach(Property item in Properties){
Console.WriteLine(item.Name)
//Get Attributes
}
}
}
Is there any way to pass just like Product.Id and use name and attributes of that? I don't sure but maybe PropertyInfo can help, I think just can pass List of Object but in that case I can't use attributes and names, what is your suggestion to handle this? or something like this? if I am wrong at all so how can I implement it?
Funny, I was just answering a similar question, or at least I think it is.
It looks like you're trying to concatenate the properties of two types into one? You need an ExpandoObject:
http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.100%29.aspx
For an implementation of a nested merge, see this:
C# deep/nested/recursive merge of dynamic/expando objects
Basically, you want a keyed list of properties, to start from. The following code will do that for any .NET object:
var props = object.GetType().GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name);
And after that it depends on what precisely it is you want to achieve - a true copy of the object, merge with another, or just maintaining the list.
You can make use of reflection in .NET here:
List<PropertyInfo> propertyList = new List<PropertyInfo>();
Type productType = typeof (Product);
propertyList.Add(productType.GetProperty("Id"));
propertyList.Add(productType.GetProperty("Title"));
TargetType target = new TargetType();
target.Properties = propertyList;
public class TargetType {
public List<PropertyInfo> Properties { get; set;}
List<object> GetAttributes()
{
List<object> attributes = new List<object>();
foreach(PropertyInfo item in Properties)
{
Console.WriteLine(item.Name);
attributes.AddRange(item.GetCustomAttributes(true));
}
return attributes;
}
}
You can use a list of PropertyInfo, List<PropertyInfo> as the type of your TargetType .Properties. To get the properties you can try it like this using Reflection.
targetType.Properties = product.GetType().GetProperties().ToList();
You can build list of properties using expression trees, e.g. you can make something like this:
var propertiesListBuilder = new PropertiesListBuilder<Product>();
propertiesListBuilder
.AddProperty(_ => _.Id)
.AddProperty(_ => _.Title);
var target = new TargetType();
target.Properties = propertiesListBuilder.Properties;
The only concern here is performance, i.e. it might be not good idea to recreate such property lists over and over again, most probably they should be cached. At the same time you'll get intellisense, compiler checks and refactoring support for your property lists.
Below is a sample implementation of this stuff.
static class PropertyInfoProvider<T>
{
public static PropertyInfo GetPropertyInfo<TProperty>(Expression<Func<T, TProperty>> expression)
{
var memberExpression = (MemberExpression)expression.Body;
return (PropertyInfo)memberExpression.Member;
}
}
class PropertiesListBuilder<T>
{
public IEnumerable<PropertyInfo> Properties
{
get
{
return this.properties;
}
}
public PropertiesListBuilder<T> AddProperty<TProperty>(
Expression<Func<T, TProperty>> expression)
{
var info = PropertyInfoProvider<T>.GetPropertyInfo(expression);
this.properties.Add(info);
return this;
}
private List<PropertyInfo> properties = new List<PropertyInfo>();
}
typeof(Product).GetProperties() would give you all (public) properties as PropertyInfo[].
See also MSDN.
Related
I am facing an issue, surely due to my lack of knowledge in the reflection process, while trying to set a "complex" class hierarchy based on Json files.
Here are my main model :
public class Names
{
public Weapons Weapons { get; set; }
public Armors Armors { get; set; }
public Utilities Utilities { get; set; }
public Names()
{
Weapons = new Weapons();
Armors = new Armors();
Utilities = new Utilities();
}
}
Each of them having a list of sub-model like this:
public class Weapons
{
public BattleAxe BattleAxe { get; set; } = new BattleAxe();
public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile();
// etc... Around 20 to 25
}
And finally the ended model which is the exact equivalent of each json files but may have very different properties :
public class BattleAxe
{
public string[] Normal { get; set; } = new string[0];
public string[] DescriptiveAdjective { get; set; } = new string[0];
public string[] Material { get; set; } = new string[0];
public string[] Type { get; set; } = new string[0];
public string[] Title { get; set; } = new string[0];
public string[] Of { get; set; } = new string[0];
public string[] NormalForTitle { get; set; } = new string[0];
}
Since the MS Json deserializer does not support the conversion to a $type as Newtonsoft before, I tried to populate the values using reflection too like this (I've removed all the null-check for code readability) :
public static void Load()
{
Names = new Names();
foreach (var category in Names.GetType().GetProperties())
{
if (category is not null && !(category.GetGetMethod()?.IsStatic ?? false))
{
var categoryType = category.PropertyType;
foreach (var item in category.PropertyType.GetProperties())
{
var itemType = item.PropertyType;
var subTypeData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json"));
var concreteObj = Activator.CreateInstance(itemType);
foreach (var key in subTypeData.Keys)
{
if (itemType.GetProperty(key) is not null && concreteObj is not null)
{
var prop = concreteObj.GetType().GetProperty(key);
var convertedValue = ConvertJsonType(subTypeData[key], subTypeData[key].ValueKind, out var isReferenceType);
// It fails here
prop.SetValue(
isReferenceType ? convertedValue : null,
!isReferenceType ? convertedValue : null
);
}
}
item.SetValue(concreteObj, null);
}
}
}
}
So it fails at the prop.SetValue(...) of the deepest object in the hierarchy with a different error depending on the type of value to set.
If it is a reference, it throws a System.Reflection.TargetException : 'Object does not match target type' Exception
And if it is value, it throw a System.Reflection.TargetException : 'Non-static method requires a target.'
Knowing that I do not have problems around the deserialization as shown here, only the fact that I use a dynamic type (and my instinct tells me it is actually the problem...)
I do not add the ConvertJsonType(...) body as it is functional and really simple
I am more interested in the 'why' than the 'how' so if you can explain me the 'theory' behind the problem, that would help quite a lot :)
Thank you!
PS: I know I can simplify the things in a more readable/performant way but I must achieve it with reflection for personal learning :)
Same for the System.Text.Json namespace, I do not intend to switch back to Newtonsoft for that
When calling SetValue(instance, value) you should pass the object which property should be set.
It's a wild guess, but you could try this:
prop.SetValue(concreteObj,
!isReferenceType ? convertedValue : null);
Because you want to fill the properties of concreteObj, not the value it self.
If you look at the object prop it was a return value of concreteObj.GetType().GetProperty(key);. If you look at it close, The GetProperty is a method from Type which isn't bound to any instance. So that's why you need to pass the instance of the object as the first parameter.
I mean this in a positive way: The itemType.GetProperty(key) is called every iteration, it will be the same value each iteration, you could bring it before the loop.
As docs state TargetException is thrown when:
The type of obj does not match the target type, or a property is an instance property but obj is null.
Passing null for obj in SetValue is valid when you are trying to set value for static property, not an instance one. Property type being a reference one has nothing to do with property being instance or static one so your call should look something like:
prop.SetValue(concreteObj, convertedValue);
Also your item.SetValue(concreteObj, null); does not look right cause concreteObj should be second argument in this call. Something like this:
item.SetValue(Names, concreteObj);
Also if you want only instance properties you can provide BindingFlags to get only instance properties:
foreach (var category in Names.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
Also I would say that category is not null check is redundant so in pair with providing BindingFlags you should remove the if completely.
I'd like to create a program to collate a variable amount of properties on an unknown object at run-time. Let me explain in more detail.
If I had an object called 'Object1' looking something like this...
public class Object1()
{
public unknownType1 Property1 { get; set; }
public unknownType2 Property2 { get; set; }
}
Then we have another object called 'Object2' that looks something like this...
public class Object2()
{
public unknownType1 Property1 { get; set; }
public unknownType3 Property3 { get; set; }
}
I'd like to collate these objects dynamically at run-time to create something like this ...
public class Object3()
{
public unknownType1 Property1 { get; set; }
public unknownType2 Property2 { get; set; }
public unknownType3 Property3 { get; set; }
}
Notice, that both objects share the same Property1.
I've already looked into ExpandoObject, however as I have no idea what type the objects or properties will be at compile time I didn't think that would work as Exapando infers the types. Does anyone have a suggestion as to how to approach this?
You can try Dictionary<string, object> where 'Key' is Property name ('Property1', 'Property2', ...) and 'Value' is Property value.
First you need to collect properties and values of your any objects somethong like this:
object[] objectsCollection = new object[] { new StringBuilder(), new ASCIIEncoding() };
foreach(var o in objectsCollection)
{
foreach(PropertyInfo pi in o.GetType().GetProperties())
{
string propertyName = ((MemberInfo)pi).Name;
var propVal = pi.GetValue(o);
//here you can collect properties and values
}
}
After you could collect your property names and values you could save it in some collection like Dictionary for example
The closest I've gotten so far is anonymous types. if you do something like this...
var Object1s = new List<Object1>() { Object1 };
var Object2s = new List<Object2>() { Object2 };
var object3s = Object1s
.Join(Object2s, o1 => o1.Property1, o2 => o2.Property1,
(o1, o2) => new
{
Property1 = o1.Property1,
Property2 = o1.Property2,
Property3 = o2.Property3
}
).First();
...it produces an anonymous type that has all three properties. While the output of this can only be assigned to object, if a type with the prerequisite properties can be provided then the new statement can be changed to account for the type.
This isn't exactly generic as the properties are all specified, however you could change the new to look like this...
(o1, o2) => new { o1, o2 }
... which would provide all the properties duplicated regardless and object3 would have to be called like this ...
object3.o1.Property1,
object3.o1.Property2,
object3.o2.Property3 ...
While not ideal, this will also work. Once I got my head into these anonymous types, my imagination filled with all sorts of possibilities. I could go on but I think you get the gist...
I have a Dictionary<string, object> which holds a property name as string and it's value as object. I also have a Bind method extension which, through reflection, sets that propery name with its corresponding value:
public static T Bind<T>(this T #this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = #this.GetType();
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
var property = sourceType.GetProperty(pair.Key,
BindingFlags.SetProperty |
BindingFlags.Public |
BindingFlags.Instance);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(#this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
return #this;
}
For instance, consider a class like this:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
Everything runs fine, except when I got a class with a property name of another object, like this:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
}
So, if I try to send a Name property name, ok, but I got problems with composite property names, like Address.PostalCode.
Can you advise a way to handle that situation?
EDIT #1:
To summarize the problem: calling sourceType.GetProperty("Name", ...) in the context of a User class instance correctly allows to set its value, but it doesn't work using a sourceType.GetProperty("Address.PostalCode", ...) in same instance.
EDIT #2:
A more complete example should be:
var user = new User{ Address = new Address() };
var values = new Dictionary<string, object>
{
{ "Name" , "Sample" },
{ "Date" , DateTime.Today },
{ "Address.PostalCode", "12345" } // Here lies the problem
}
user.Bind(values);
My guess is that Convert.ChangeType only works for objects implementing IConvertible. Thus, I'd just add a check, and only use Convert.ChangeType if pair.Value has a type that implements IConvertible. Furthermore, afaik Convert does not use overloaded conversion operators, so you can save this check whenever pair.Value is not a struct, i.e.
object value;
if (pair.Value == null) {
value = null;
} else {
value = pair.Value.GetType().IsStruct ? Convert.ChangeType(pair.Value, propType) : pair.Value;
}
...
There are many binding engines out there, WPF, ASP.NET MVC, winforms in the core .NET and who knows how many others, you can check out all their source codes and documentation about their syntax.
Let's see the most simple case. Let's say that the variable X holds an object and you have the binding expression "A.B.C". Let's split up the binding path, the first part is "A". So you use reflection to get the property named "A" in X, and you put that other object into X. Now comes the second part, "B", so let's find a property named "B" in (the new) X. You find that, and put that into X. Now you get to the final part, "C", and now you can either read or write that property in X. The point is that you don't need recursion or anything, it's just a simple loop, you iterate over the parts of the binding expression, evaluate them, and you keep the current object in the same variable.
But the fact is that it can get much more complex than that. You could ask for array indexing, like "A.B[2].C". Or what if you have a path "A.B", and X.A is null, what do you do? Instantiate X.A, but what if it lacks a public parameterless constructor?
I want you to see that it can be a very complex problem. You have to specify a syntax and rules, and then implement that. You didn't specify in your question the exact syntax and rules you want to use. And if it happens to be more than the simple case I mentioned above, then the solution could be too lengthy.
I was able to solve it identifying if the property name have a period and recurring it:
public static T Bind<T>(this T #this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = #this.GetType();
var binding = BindingFlags.Public | BindingFlags.Instance;
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
if(pair.Key.Contains("."))
{
var property = sourceType.GetProperty(
pair.Key.Split('.').First(),
binding | BindingFlags.GetProperty);
var value = property.GetValue(#this, null);
value.Bind(new Dictionary<string, object>
{
{
String.Join(".", pair.Key.Split('.').Skip(1).ToArray()),
pair.Value
}
});
}
else
{
var property = sourceType.GetProperty(pair.Key,
binding | BindingFlags.SetProperty);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(#this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
}
return #this;
}
Usage:
var user = new User {Address = new Address{ User = new User() }};
var values = new Dictionary<string, object>()
{
{"Name", "Sample"},
{"Date", DateTime.Today},
{"Address.PostalCode", "12345"},
{"Address.User.Name", "Sub Sample"}
};
user.Bind(values);
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
public User User { get; set; }
}
I have got three classes as follows:
public class TestA
{
public string Str1 { get; set; }
public string Str2 { get; set; }
public List<TestB> LstTestBs { get; set; }
public TestC ObjTestC { get; set; }
}
public class TestB
{
public string Str3 { get; set; }
public string Str4 { get; set; }
}
public class TestC
{
public string Str5 { get; set; }
}
I have tried:
var prop = typeof (TestA).GetProperties();
But, it is giving only the PropertyInfo for the four members inside TestA. I need to get the PropertyInfo for all the members in the TestA, TestB and TestC classes.
Please help...
Thanks in advance,
San
If you put all your classes in the same namespace, you can collect the properties by enumerating the classes in the namespace, instead of mining the property structure:
Getting all types in a namespace via reflection
Thanks for the help everyone.
I have got the answer.
var prop = typeof (TestA).GetProperties();
for (int i=0;i<prop.Count();i++)
{
var propertyInfo = prop[i];
if (propertyInfo.PropertyType.Namespace != "System")
{
if (propertyInfo.PropertyType.IsGenericType &&
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof (List<>))
{
Type itemType = propertyInfo.PropertyType.GetGenericArguments()[0];
var listObjectProperties = itemType.GetProperties();
prop = prop.Union(listObjectProperties).ToArray();
}
else
{
var childProp = propertyInfo.PropertyType.GetProperties();
prop = prop.Union(childProp).ToArray();
}
}
}
SLaks is right. You should do this recursively. See wikipedia's article on Recursion for more information on the concept. For example, in your case, this is the general idea:
public void AddPropertiesAndChildPropertiesToList(Type type, List<PropertyInfo> list)
{
var properties = type.GetProperties();
list.AddRange(properties);
foreach (var property in properties)
{
// recursive methods are ones that call themselves, like this...
AddPropertiesAndChildPropertiesToList(property.PropertyType, list);
}
}
Note that this example is lacking several things:
Most importantly, it has no guard against infinite recursion. You could fix this by keeping track of where you'd already been with a Stack<Type> alreadyVisited parameter. If you find you've been asked to add the list of properties for a type you've already visited, just return out of the method instead, or throw an exception.
As I mentioned in your other related question, for your purposes you really need to be keeping track of property chains, not just properties. The alreadyVisited stack would be useful here, too.
It won't handle your List<TestB> in any useful way. For that, you probably need to figure out whether the type has an indexer, and then the properties of the type that is returned by that indexer.
I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).