Select property by lambda by PropertyInfo variable - c#

This is my POCO object:
public class ExampleTestOfDataTypes
{
public float FloatProp { get; set; }
public BoolWrapper BoolProp2 { get; set; }
}
This is configuration file of the POCO
public class ExampleTestOfDataTypesConfig : EntityTypeConfiguration<ExampleTestOfDataTypes>
{
public ExampleTestOfDataTypesConfig()
{
this.Property(x=>x.FloatProp).HasColumnName("CustomColumnName");
}
}
This is definition of EntityTypeConfiguration (the property configuration is just for example)
ExampleTestOfDataTypesConfig config = new ExampleTestOfDataTypesConfig();
I need to go through all the properties of class ExampleTestOfDataTypes, find all the properties which dataTypes are derived from Wrapper (BoolWrapper is) and then get these properties using lambda expression. Or anyhow select them by config.Property(...)
Type configPocoType = config.GetType().BaseType.GetGenericArguments()[0];
var poco = Activator.CreateInstance(configPocoType);
foreach (System.Reflection.PropertyInfo property in poco.GetType().GetProperties())
{
if (property.PropertyType.BaseType!=null&&
property.PropertyType.BaseType == typeof(Wrapper)
)
{
//TODO: Set property
//config.Property(x=>x.[What here]); //?
}
}
Thanks

Update
I didn't notice that the Property method is not your own implementation, sorry. It looks like you have to create an expression manually. This should work, or at least be close enough:
var parameter = Expression.Parameter(configPocoType);
var lambda = Expression.Lambda(
Expression.MakeMemberAccess(parameter, property),
parameter);
config.Property(lambda);
Original answer
It looks pretty likely that your existing Property method just uses an Expression to read the name of the property while maintaining compile-time safety. Most of the time such methods use reflection to pull out the property name into a string and then go on reflecting using the string name (possibly by calling another overload of Property that accepts a string).
Therefore, a reasonable approach is to invoke this other overload yourself, since your code already has a PropertyInfo in hand from which you can immediately get the property name.
If you only have one Property method, refactor by splitting it into two parts: one that pulls the name out of the Expression and one that works with the name; you can then call the second directly from your code.

Related

How to iterate through an ICollection type property with reflection

The title covers a very tiny part of what I'm trying to achieve, so be informed ahead. I'm trying to build a generic way of properly updating collection properties of an entity when that entity itself is updated. In the nutshell I want to do something similar to the approach explained here but I want to go generic way. For that I have created an attribute called EntityCollectionPropertyAttribute and marked those properties of entities that
I need to be updated too. Here's an example:
public class Teacher{
public int TeacherId{get;set;}
public string Name{get;set;}
}
public class Student{
public int StudentId{get;set;}
public string Name {get;set;}
[EntityCollectionProperty]
public virtual ICollection<Teacher> Teachers{get;set;}
}
public bool IsPropertyAnEntityCollection(PropertyInfo prop){
return Attribute.IsDefined(prop, typeof(EntityCollectionPropertyAttribute));
}
public void Update<T>(T entity)where T:class{
DbEntityEntry entry = MyDbContext.Entry(entity);
foreach (var prop in entry.Entity.GetType().GetProperties())
{
if(IsPropertyAnEntityCollection(prop)){
//Here's where I get stuck
}
}
}
Lets say the parent entity that has been updated is a Student. Besides Name (and possibly ID) properties, I need the Teachers to be updated as well. So in the commented area I need something like this:
var updatedTeachers=studentEntity.Teachers.ToList();
but of course generic way. I will also have to look inside the DbContext for the teachers DBSet independently. So I will need something like this too:
var exisitingTeachers=MyDbContext.Teachers.ToList();
Any ideas how to do this?
You can call ToList method by passing property value prop.GetValue(entity) in this method:
private IList CollectionToList(object value)
{
var collectionType = value.GetType().GenericTypeArguments.First();
var method = typeof(Enumerable).GetMethod("ToList");
var genericMethod = method.MakeGenericMethod(collectionType);
return (IList)genericMethod.Invoke(null, new[] { value });
}
In this way you will be able to iterate through collection.
If type of property value List or Array and there is no need in creating new collection you can just cast value to IList and iterate through it.
For getting values from database context you can use Set method:
var dbset = MyDbContext.Set(prop.PropertyType.GenericTypeArguments.First());
dbset also could be passed to CollectionToList method, but in that way you will load all table rows from table which could take a lot of time and memory.

Using reflection to cast an IList<Interface> to List<T>

I am working on a WCF service and I have run into a bit of a snag mapping my entities to my DTO. Consider the following
namespace Foo.Entities
{
public class Order : IOrder
{
public string Name { get;set; }
public string Address { get;set; }
public IList<ILocation> Locations { get;set; }
}
}
namespace Foo.DTO
{
[DataContract]
public class Order
{
[DataMember]
public string Name { get;set; }
[DataMember]
public string Address { get;set; }
[DataMember]
public List<Location> Locations { get;set; }
}
}
This is all very straightforward: DTO.Order is what I am returning from my endpoint and Entities.Order is what I am using internally (I am using DI / IOC) for business logic, data operations, etc. Since my business layer returns types from the Entities namespace, but the endpoint returns types from the DTO namespace I wrote a small mapping method that will take one type and map it to another type like so:
public TTarget MapObject<TSource, TTarget>(TSource source, TTarget target)
where TSource : class
where TTarget : class
{
foreach (var prop in source.GetType().GetProperties())
{
var targetProp = target.GetType().GetProperty(prop.Name, BindingFlags.Public | BindingFlags.Instance);
if(targetProp == null || !targetProp.CanWrite) continue;
if (prop.PropertyType.GetGenericTypeDefinition() == typeof (IList<>))
{
??
}
else{ targetProp.SetValue(target, prop.GetValue(source)); }
}
return target;
}
I then call this method like so:
factory.MapObject(Entities.DealerOrder, new GTO.DealerOrder())
where Entities.DealerOrder represents an instantiated object that contains data.
Everything works fine until I get to the property of type IList and I am at a loss at how to convert the IList to List. I know what needs to happen but all of the documentation I have read thus far hasn't pointed me in the right direction.
The pseudo is
if (prop.PropertyType.GetGenericTypeDefinition() == typeof (IList<>))
{
var lst = new List<type of targetProp>()
foreach(var val in prop.GetValue())
{
var item = new Location() (I have to figure out this initialization based on the List type of targetProp. In this case it would be List<Location>)
var retval = MapObject(val, item);
lst.Add(retval);
}
targetProp.SetValue(target, lst);
}
I am not sure if what I want to do is even possible. I know that Generics and Reflection don't mix well so if there is a solution it might be overly complex for what I am really trying to accomplish. If worse comes to worse I can put a static method on each of my DTO's that will accept the source type as a parameter and return an instance of the DTO, but I want to avoid having to manually map the fields from the Entity to the DTO if at all possible.
Any help is greatly appreciated.
You can use targetProp.GetGenericArguments()[0]; to get the type of item you want to map your collection content to.
You can use Activator.CreateInstance to create List<T> with T known at runtime at not at compile time.
You can use Activator.CreateInstance to create instance of the type you want to map to.
You can't rely on type inference when calling MapObject anymore. You need to create proper generic method via reflection here too, and call it.
You can't simply call Add on the list, because you don't know what kind of list it is. You can cast it to ICollection and call Add on it instead.
Can't you just use something like AutoMapper? Those are problems people already solved, why don't you use their work?

Generic function that takes properties as parameters

I want to write a generic function that takes an object and a series of properties of this object. Inside the function I would like to select a new anonymous object that is simply just those properties of the passed in object.
I want to do something like this:
public class SimpleClass
{
public DateTime ADate {get; set;}
public string StringHere {get; set;}
public int ANumber {get; set;}
}
var testObj = new SimpleClass();
// set values here on testObj properties
DoStuffHere(testObj, StringHere, ANumber);
I could pass in the properties as strings and then use reflection to get the properties from the passed in object, but I wanted to know if there was some way I could pass in the properties themselves so I could have intellisense and compile time checking to prevent bad property names. I would like my getNewClass function to take any type of object, and such, be generic.
Edit: I am not returning a new anonymous type. I think my function name was making it sound that way. I am going to be selecting a new anonymous type internally from a list of that specified testObj and generating a PDF from those properties.
Defining an anonymous type is actually very complicated, and trying to do it just with the names is somewhat challenging. Essentially what you want already exists, but in regular C# - so for a single object:
var obj = new { testObj.StringHere, testObj.ANumber };
Or for multiple objects:
var projection = from obj in sequence
select new { obj.StringHere, obj.ANumber };
That's about as succinct as you'll get. You could add a generic method that took a Func<,> of some kind, but it wouldn't be any cleaner than the above.
It isn't useful to have:
var obj = SomeMagicMethod(obj, "StringHere", "ANumber");
because SomeMagicMethod could only usefully return object - our obj variable would be largely unusable.
If you don't need to return the object from the method, then you could use either of:
SomeMagicMethod<T>(T value) {
...
}
...
SomeMagicMethod(new {testObj.StringHere, testObj.ANumber });
or:
SomeMagicMethod<TFrom, TTo>(TFrom value, Func<TFrom, TTo> selector)
{
TTo actualVal = selector(value);
...
}
...
SomeMagicMethod(testObj, x => new {x.StringHere, x.ANumber });
Personally, I think the first is easier - the func in the second is overkill.
You could also just use reflection...
SomeMagicMethod(object obj, params string[] names)
{
foreach(var name in names) {
object val = obj.GetType().GetProperty(name).GetValue(obj);
// ...
}
}
//...
SomeMagicMethod(testObj, "StringHere", "ANumber");
you can pass them as lambda:
GetNewClass (testObj, ()=>StringHere, ()=> ANumber);
and have a signature for GetNewClass like
void GetNewClass (object, Expression<Func<object>> expr0, Expression<Func<object>> expr1);
You can then get the property quite easily.
You can use Linq expressions for that.
(note: it's possible you need to modify a few things in the snippet below, this is of the top of my hat):
public void getNewClass(Object testObj, params MemberExpression Fields[])
{
foreach(MemberExpression field in Fields)
{
// Get the name
var name = field.Member.Name;
// get the value
var member= Expression.Convert(field, typeof(object));
var lambda= Expression.Lambda<Func<object>>(member);
var fnc= lambda.Compile();
var value = fnc();
}
}
This snippet show how to get the name of the property and the value. It can be called like this:
getClass(someObj, obj => obj.SomeProperty, obj.SomeOtherProperty);

How to combine MemberExpression instances in C# for a LambdaExpression?

Given a class like this:
public class AnEntity
{
public int prop1 { get; set; }
public string prop2 { get; set; }
public string prop3 { get; set; }
}
I am able to generate a lambda expression that selects one property like this:
ParameterExpression pe = Expression.Parameter(typeof(AnEntity), "x");
MemberExpression selectClause = Expression
.MakeMemberExpression(
pe,
typeof(AnEntity).GetProperty(prop2)); // selecting prop2
var selectLambda = Expression.Lambda<Func<AnEntity, object>>(selectClause, pe);
I can then use the lambda expression like this:
IQueryable<AnEntity> myEntities = dbContext.MyEntities.AsQueryable();
var results = myEntities.Select(selectLambda);
How can I add a second select clause to the selectLambda? For example, how would I select both prop2 and prop3?
Below is a fleshed out example of what "usr" described in his solution using MemberInitExpression.
For my solution, I am going to provide you with a new class that you will want write the expression result into. In reality, this could be the POCO for the entity itself, but by specifying a different class, it is clearer which class is the class you are projecting into as compared to the class you are projecting from. As "usr" mentioned, you can also try to use Tuple or other constructs. My current favorite is to use the extra code I appended to the bottom to create a new type dynamically. This is a bit more flexible than Tuple, but it has some disadvantages in that it requires Reflection to access it for the most part.
Class to project into:
public class Holder
{
public int Item1{ get; set; }
public string Item2 { get; set; }
public string Item3 { get; set; }
}
Code to create expression:
ParameterExpression paramExp = Expression.Parameter(typeof(AnEntity));
NewExpression newHolder = Expression.New(typeof(Holder));
Type anonType = typeof(Holder);
MemberInfo item1Member = anonType.GetMember("Item1")[0];
MemberInfo item2Member = anonType.GetMember("Item2")[0];
MemberInfo item3Member = anonType.GetMember("Item3")[0];
// Create a MemberBinding object for each member
// that you want to initialize.
MemberBinding item1MemberBinding =
Expression.Bind(
item1Member,
Expression.PropertyOrField(paramExp, "prop1"));
MemberBinding item2MemberBinding =
Expression.Bind(
item2Member,
Expression.PropertyOrField(paramExp, "prop2"));
MemberBinding item3MemberBinding =
Expression.Bind(
item3Member,
Expression.PropertyOrField(paramExp, "prop3"));
// Create a MemberInitExpression that represents initializing
// two members of the 'Animal' class.
MemberInitExpression memberInitExpression =
Expression.MemberInit(
newHolder,
item1MemberBinding,
item2MemberBinding,
item3MemberBinding);
var lambda = Expression.Lambda<Func<AnEntity, Holder>>(memberInitExpression, paramExp);
Finally, how you would call the expression:
IQueryable<AnEntity> myEntities = dbContext.MyEntities.AsQueryable();
var results = myEntities.Select(selectLambda);
Here is some additional code if you wanted to define a type dynamically for the return value:
public static Type CreateNewType(string assemblyName, string typeName, params Type[] types)
{
// Let's start by creating a new assembly
AssemblyName dynamicAssemblyName = new AssemblyName(assemblyName);
AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule(assemblyName);
// Now let's build a new type
TypeBuilder dynamicAnonymousType = dynamicModule.DefineType(typeName, TypeAttributes.Public);
// Let's add some fields to the type.
int itemNo = 1;
foreach (Type type in types)
{
dynamicAnonymousType.DefineField("Item" + itemNo++, type, FieldAttributes.Public);
}
// Return the type to the caller
return dynamicAnonymousType.CreateType();
}
The way to answer questions like these is to write the pattern you want to build in strongly typed C# (select new { x.p1, x.p2 }) and use the debugger to look at the expression tree. Then you build that tree yourself.
What you will find is a MemberInitExpression instantiating a class with the two properties p1 and p2 that are being initialized from x.
You have to supply such a class somehow. Either define it yourself (class X { string p1, p2; }) or use a Tuple-like construct. Or an object[].
Understand that you can only have one return value. So it needs to encapsulate multiple values.
The easiest way is going to be using an object[]. Look at how the C# compiler does it.
There is no "second select return value". You get to return one value, which might be an aggregate.
The C# language provides a lot of syntactic sugar in this area, in the form of LINQ and anonymous types. To return an aggregate via a lambda built by expression trees, you'll have to create a type to hold all the different values (just like the C# compiler does behind the scenes when it sees an anonymous type) and then call its constructor passing the multiple values you want to return (this is actually somewhat easier than what C# does behind the scenes, which is call a bunch of property setters). At runtime, there are no anonymous types. They all get named, if not by the programmer then by the compiler.
In fact you can probably use a Tuple<Type1, Type2, Type3> instead of making a class especially for the purpose. But then your member properties won't be named nicely.

C# setting property values through reflection with attributes

I am trying to build an object through an attribute on a classes property that specifies a column in a supplied data row that is the value of the property, as below:
[StoredDataValue("guid")]
public string Guid { get; protected set; }
[StoredDataValue("PrograGuid")]
public string ProgramGuid { get; protected set; }
In a Build() method on a base object, I am getting the attribute values set on these properties as
MemberInfo info = GetType();
object[] properties = info.GetCustomAttributes(true);
However, at this point I am realising the limitation in my knowledge.
For a start, I don't appear to be getting back the correct attributes.
And how do I set these properties through reflection, now that I have the attributes? Am I doing / thinking something fundamentally incorrect?
There are a couple of separate issues here
typeof(MyClass).GetCustomAttributes(bool) (or GetType().GetCustomAttributes(bool)) returns the attributes on the class itself, not the attributes on members. You will have to invoke typeof(MyClass).GetProperties() to get a list of properties in the class, and then check each of them.
Once you got the property, I think you should use Attribute.GetCustomAttribute() instead of MemberInfo.GetGustomAttributes() since you exactly know what attribute you are looking for.
Here's a little code snippet to help you start:
PropertyInfo[] properties = typeof(MyClass).GetProperties();
foreach(PropertyInfo property in properties)
{
StoredDataValueAttribute attribute =
Attribute.GetCustomAttribute(property, typeof(StoredDataValueAttribute)) as StoredDataValueAttribute;
if (attribute != null) // This property has a StoredDataValueAttribute
{
property.SetValue(instanceOfMyClass, attribute.DataValue, null); // null means no indexes
}
}
EDIT: Don't forget that Type.GetProperties() only returns public properties by default. You will have to use Type.GetProperties(BindingFlags) to get other sorts of properties as well.

Categories

Resources