Reflection and inheritance issue c# - c#

I'm trying to determine the MemberExpression of all the parts of a structure with the use of reflection. These are some of the objects to illustrate the issue:
public class Entity
{
public Part FirstPart { get; set; }
}
public class Part
{
public int Id { get; set; }
}
public class SubPart : Part
{
public int ExtraProperty { get; set; }
}
The function I used to determine the MemberExpression of every component, works fine for the following object structure:
Entity entity = new Entity() { FirstPart = new Part() { Id = 1 } };
The function is:
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.Id").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
But this doesn't work for the following, due to inheritance:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
In order to retrace the properties we need to change the path to "FirstPart.ExtraProperty":
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
The error message states that: 'ExtraProperty' is not a member of Part. Does anyone have an idea how to overcome this issue?

You can't. Think of expressions as code, which is compiled at runtime instead of compile time. There is no magic and similar rules apply (expressions are low level and more restrictive, so many syntactic sugar which are available at C# code level are not available in expressions). Saying that, since entity.FirstPart.ExtraProperty is not valid in C# code, it won't be valid in expressions as well.
You could insert explicit cast - but then you assume that instance will actually be of type SubPart, so why don't you define member FirstPart of type SubPart instead of Part. Or you could create type test logic using TypeIs expression and then cast the same way as you would in C# code.
EDIT:
After rereading your problem, I see that what you actually are trying to implement is property walker over arbitrary objects. So TypeIs expression will not help you here since it requires that type, which you are testing against, is known at compile time. But in your case there can be arbitrary class derived from Part in FirstPart member with arbitrary additional properties. In this case, there is no other option, but to evaluate each property access one by one and retrieve actual type from intermediate values. For example:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
object currentObjectInChain = entity;
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
foreach (var property in childProperties)
{
if (currentObjectInChain == null)
{
throw new ArgumentException("Current value is null");
}
var type = currentObjectInChain.GetType();
var param = Expression.Parameter(type);
var lambda = Expression.Lambda(
Expression.PropertyOrField(param, property),
param).Compile(); // cache based on type and property name
currentObjectInChain = lambda.DynamicInvoke(currentObjectInChain);
}
At the end of loop currentObjectInChain will hold your value.

Related

Subclass Reflection type error

I'm currently having some issues with a method I made. I use reflection to run through my class and get all it's properties. I use this to cast my models to DTO and vice-versa.
The problem I am encountering is that, whenever my class has another class as an attribute, I get an error.
Object of type 'UserTypeProxy' cannot be converted to type 'MyNamespace.DTO.UserTypeDto'.
This is my code:
public static T Cast<T>(object myobj)
{
Type _objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in _objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
var propy = myobj.GetType().GetProperty(memberInfo.Name);
value = propy.GetValue(myobj, null);
propertyInfo.SetValue(x, value, null); //<-- this is the line that gives the error
}
return (T)x;
}
As a previous commenter states, this is not the kind of code you should be writing/maintaining yourself. Frameworks like AutoMapper were built specifically to solve the problem you are attacking - converting model objects to DTOs. The right long-term choice would be to start leveraging such a framework instead of reinventing the wheel.
In the meanwhile the following code is a short-term solution for your issue. Keep in mind that while this may solve the specific case you mention in your question, object mapping has many corner cases and eventually you will run into another. I would recommend only using this as a temporary fix until you migrate to using AutoMapper or a similar framework.
Based on your description and your code, here is an example which models your failure:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This fails saying that UserModel cannot be converted to UserDto
var userDto = Cast<UserDto>(user);
}
class UserModel
{
public String Name { get; set; }
public int Age { get; set; }
public UserModel Buddy { get; set; }
}
class UserDto
{
public String Name { get; set; }
public int Age { get; set; }
public UserDto Buddy { get; set; }
}
The problem is that the Buddy property, unlike all the others, has a different type in the model and DTO classes. A UserModel is simply not assignable to a UserDto. The only exception to this is if the value is null.
For properties which are class types, instead of setting the target equal to the source you need to map the source type to the target type: UserModel -> UserDto. This can be done with a recursive call.
Before I show you the code which solves this issue, let's talk about naming for a minute. Calling your function Cast() is very misleading. The operation we are really doing here is taking some source object and mapping its property values onto some target object of a specific type (with possible recursive mappings for properties which are class types).
Given this terminology, here is some updated code which solves this specific issue:
public static T MapProperties<T>(object source)
{
return (T)MapProperties(source, typeof(T));
}
public static object MapProperties(object source, Type targetType)
{
object target = Activator.CreateInstance(targetType, nonPublic: false);
Type sourceType = source.GetType();
var sourcePropertyLookup = sourceType.GetProperties().ToDictionary(p => p.Name);
var targetPropertyLookup = targetType.GetProperties().ToDictionary(p => p.Name);
var commonProperties = targetPropertyLookup.Keys.Intersect(sourcePropertyLookup.Keys);
foreach (var commonProp in commonProperties)
{
PropertyInfo sourceProp = sourcePropertyLookup[commonProp];
PropertyInfo targetProp = targetPropertyLookup[commonProp];
object sourcePropValue = sourceProp.GetValue(source);
if(sourcePropValue == null || targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
targetProp.SetValue(target, sourceProp.GetValue(source));
}
else
{
object mappedValue = MapProperties(sourceProp.GetValue(source), targetProp.PropertyType);
targetProp.SetValue(target, mappedValue);
}
}
return target;
}
You can use this in the same way you've used your previous code:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This works!
var userDto = MapProperties<UserDto>(user);
}
Aside from some optimizations the key differences from your code is in the if-else block. There we check if we can assign the source value to the target directly, in which case we do what your code was doing so far. Otherwise it assumes we need to recursively map the value over. This new section is what solves the issue of converting a source property of a model class type to a target property of a DTO class type.

Given this lambda, how can I write it manually with expression trees?

class Program
{
static void Main(string[] args)
{
Expression<Func<string[], Poco>> exp = a => new Poco { MyProperty1 = a[0], MyProperty2 = a[1], MyProperty3 = a[2] };
var lambda = exp.Compile();
var output = lambda(new[] {"one", "two", "three"});
Console.WriteLine(output.MyProperty1);
}
}
class Poco
{
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
I'm not interested in the part calling the lambda, thats just for completeness. I get completely lost trying to navigate expression trees, and this might teach me how to fish.
private static Expression<Func<string[], Poco>> CreateExpr()
{
ParameterExpression paramExpr = Expression.Parameter(typeof(string[]), "a");
var newExpr = Expression.New(typeof(Poco));
var memberExprs = Enumerable.Range(0, 3)
.Select(i =>
{
string propertyName = "MyProperty" + (i + 1);
var property = typeof(Poco).GetProperty(propertyName);
Expression.Bind(property, Expression.ArrayIndex(paramExpr, Expression.Constant(i)));
});
var expr = Expression.MemberInit(newExpr, memberExprs);
return Expression.Lambda<Func<string[], Poco>>(expr, paramExpr);
}
I don't have time right now to translate the complete tree, but one thing you can do is compile your code and then use ildasm (or reflector etc) to look at what the compiler's doing. You can't always do exactly the same in your own code, but it gives you an idea of the kind of expressions you'll want. In particular, in this case you'll want:
Expression.Parameter to create the parameter (a)
Expression.New to create the new instance
Expression.Bind to create a property assignment
Expression.MemberInit to assign the properties in the new object
Expression.ArrayIndex for each array access (a[0] etc)
Expression.Constant for the array indexes themselves (0, 1, 2)
Expression.Lambda to create an Expression<TDelegate> for the whole thing
If I get time later on, I'll try to construct a complete working example.

HowTo use Dynamic linq with an index instead of a property name

I have an ICollection with objects:
private ObservableCollection<ViewItem> items;
The viewItems have no properties. The data will be accessed via an index with
public object this[int index] {
get{ .... }
set {....}
}
I have a geneal class for filtering. The linq with properies will work fine. I use (the important code only):
Queryable = CreateQueryable((IEnumerable<object>)mItemsSource.SourceCollection, ItemType);
mQuery = Queryable.Where(filterString).Cast<object>();
ilteredCollection = mQuery.ToList();
with:
private static IQueryable CreateQueryable(IEnumerable<object> collection, Type itemType)
{
if (itemType == null) return null;
var queryableList = collection.AsQueryable();
return queryableList.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Cast",
new Type[] { itemType },
queryableList.Expression));
}
So I can use a filter string like: Id>10 or Name="abc"
where Id and Name are property names.
But I have also Object in another collection which only have access via index. so I have an where string like:
[0]>10 or [1]="abc"
I didn't find any solution. The only hint I could find is to use:
new(it([idx] as Type)
where idx is element index and Type is a type of this element
e.g.
[0]>10 --> new(it[0] as object)>10
But than I get the error:
{"Operator '=' incompatible with operand types 'DynamicClass1' and 'Int32'"}
Useing a string in my filter like:
new(it[0] as object)>"10"
than the error is:
{"Operator '=' incompatible with operand types 'DynamicClass1' and 'string'"}
So - how can I solve this problem. Because this is a general Filterclass I also don't know the type. So in the as statement I can only use object or something like this.
I hope anyone can help me. Perhaps the dynamic keyword of C# 4.0 will help??
BTW a workaround will be to impement a wrapper in each class with indexer, but this will be a lot of stupid work. And that is something a real programmer don't like ;). I am sure there is a solution!
Cheer up !!
First of all -- How to access Current Instance ?
When parsing a lambda expression with a single unnamed parameter, the members of the unnamed parameter are automatically in scope in the expression string, and the current instance given by the unnamed parameter can be referenced in whole using the keyword it. For example,
customers.Where("Country = #0", country);
is equivalent to
customers.Where("it.Country = #0", country);
Above concept has been explained here.
From above explanation, we can now access the indexer property as it[#0] where #0 is value of index to be passed, as explained below.
//Consider below class
public class Product
{
private NameValueCollection collection = new NameValueCollection();
public string Company { get; set; }
public string Distributor { get; set; }
public int ID { get; set; }
...
public string this[string index]
{
get { return collection[index]; }
set { if(!string.IsNullOrEmpty(value)) collection[index]=value; }
}
}
//Main Code
List<Product> list = new List<Product>();
Product product = new Product() { Company = "Nestle", Distributor = "xyz", ID = 1 };
product["Name"] = "Maggi";
list.Add(product);
var filteredList = list.AsQueryable().Where("it[#0]=#1", "Name", "Maggi"); //Accessing the current item by indexer property
foreach (Product productItem in filteredList)
{
Console.WriteLine(productItem.Company);
}
Hope this helps you !! :)
Your usage of new keyword is wrong.
It does not cast object (nor does as).
Keyword new is used to create new object of anonymous class with the specified properties.
Thus new(it[idx] as Type) will create new object with property Type having the value it[idx]. It is equivalent to C#'s: new { Type = this[idx] }.
As I have already pointed out in Dynamic linq: Is there a way to access object data by index?, you need to cast it in the following manner: Int32(it[0]) > 10 for your pseudo-query [0] > 10.

How to get more than one column in Linq

here is my code:
firstAnswer = p.Answers.Select(z => z.vountcount, z.isSelected).FirstOrDefault()
In the select statement, it returns a syntax error. I am trying to get more then one column.
var firstAnswer = p.Answers.FirstOrDefault().Select(new { VountCount = z.vountcount, IsSelected = z.isSelected });
You must specify a type. Var is the keyword allowing you to instantiate an anonymous type.
You'll either have to create a type or use anonymous types to capture that result:
Anonymous types:
var firstAnswer = p.Answers.Select(z => new { vountcount = z.vountcount, isSelected = z.isSelected }).FirstOrDefault();
The explicit naming is in most cases optional.
Using the var keyword here ensures, that you can assign that anonymous result. If you want to hand that result to some method, it'll get difficult with anonymous types.
Dedicated type:
public class ReducedAnswer
{
public int vountcount { get; set; }
public bool isSelected { get; set; }
public ReducedAnswer()
{
}
}
ReducedAnswer firstAnswer = p.Answers.Select(z => new ReducedAnswer { vountcount = z.vountcount, isSelected = z.isSelected }).FirstOrDefault();
Kept it close to typical LINQ model classes and your naming. Note the use of the type in front of firstAnswer. You could go with var here as well.

C# Dynamically apply evaluation rule on a collection

I have a fixed-length data file need to persistence into database. I use a XML file to define the length for the fields and use a list of FieldItem class to store the data.
class FieldItem
{
public string ObjectName {get; set;}
public string ObjectProperty {get; set;}
public string ObjectValue {get; set;}
public FieldItem()
{
}
}
So the FieldItem will look like
var fieldItem = new FieldItem
{
ObjectName = "Company",
ObjectProperty = "Name",
ObjectValue = "ABC Corp."
}
After get the list of FieldItem, I will do refection to create Company and other domain objects to save them into database.
But before they are saved into database, I have some business rules needed to be applied to validate the data line. My data line will look like:
var fieldItemList = new List<FieldItem>(){
new FieldItem {
ObjectName="Company",
ObjectProperty = "Name",
ObjectValue = "ABC"
}
new FieldItem{
ObjectName = "Product",
ObjectProperty = "ProductCode",
ObjectValue ="XYZ0123"
new FieldItem{
ObjectName = "Product",
ObjectProperty = "ProductName",
ObjectValue ="Christmas Tree"
}
// other FieldItem objects...
}
For example, my rule is to check if the company == "ABC" and ProductCode == "XYZ0123". Those rules are created by users and stored as a string in the database. What I am doing right now is to use Microsoft's System.Linq.Dynamic to evaluate the rule, such as
string validationRule = " (ObjectName == \"Company\" And ObjectProperty=\"CompanyName\" And ObjectValue = \"ABC Corp.\") And (ObjectName == \"Product\" And ObjectProperty=\"ProductCode\" And ObjectValue = \"XYZ0123\") ";
var query = fieldItemList.AsQuerable().Where(validationRule);
then check if it has one row return to tell if that data row has passed the rule or not.
Obviously, it is too verbose. Do you have any better suggestion? What should I do if I only like my rule expression like: "Company.CompanyName = 'ABC Corp.' and Product.ProductCode = 'XYZ001'"?
RE: "What should I do if I only like my rule expression like: "Company.CompanyName = 'ABC Corp' and Product.ProductCode = 'XYZ001'"?
Map this user-friendly query: "Company.CompanyName = 'ABC Corp' AND Product.ProductCode = 'XYZ001'" to something that is friendly to your data structure(FieldItem):
"ObjectName = 'Company' AND ObjectProperty = 'CompanyName' AND ObjectValue = 'ABC Corp' OR ObjectName = 'Product' AND ObjectProperty = 'ProductCode' AND ObjectValue = 'XYZ001'"
How to know if the user's rules passed the rules or not? Count the number of conditions, if it matches the count of results of fieldItemList.AsQueryable().Where(itemFieldFriendlyQuery), then the data line is valid based on user's rules.
some rudimentary mapper(use regular expression or roll your own parser to make the following code truly valid):
public Form3()
{
InitializeComponent();
string userFriendlyQuery = "Company.CompanyName = 'ABC Corp' AND Product.ProductCode = 'XYZ001'";
string[] queryConditions = userFriendlyQuery.Split(new string[]{" AND "},StringSplitOptions.None);
int conditionsCount = queryConditions.Length;
string itemFieldFriendlyQuery = string.Join(" OR ",
queryConditions.Select(condition =>
{
var conditionA = condition.Split(new string[] { " = " }, StringSplitOptions.None);
var left = conditionA[0];
var leftA = left.Split('.');
string objectName = leftA[0];
string objectProperty = leftA[1];
var right = conditionA[1];
return string.Format("ObjectName = '{0}' AND ObjectProperty = '{1}' AND ObjectValue = {2}",
objectName, objectProperty, right);
}
).ToArray());
MessageBox.Show(itemFieldFriendlyQuery);
// outputs: "ObjectName = 'Company' AND ObjectProperty = 'CompanyName' AND ObjectValue = 'ABC Corp' OR ObjectName = 'Product' AND ObjectProperty = 'ProductCode' AND ObjectValue = 'XYZ001'"
bool isValid = fieldItemList.AsQueryable().Where(itemFieldFriendlyQuery).Count() == conditionsCount;
MessageBox.Show(isValid.ToString());
}
Consider using the FileHelpers library to parse your file directly into regular .NET domain objects. I believe this, combined with your Dynamic Linq approach, will provide you with the syntax that you're looking for.
You could have them store it in the database as C# code expression, and then use the CodeDom classes to parse and compile it into a type which exposes a method which will run the comparison.
That will create a lot of temp assemblies, which would be a PITA to manage, IMO.
Rather, I'd still use the C# notation for expression syntax, but instead of compiling it into code, create a lambda expression dynamically (through the Expression class) and then compile into a delegate that takes all the parameters (you will need a mapping mechanism here) and then just call it using your created types.
As an aside: why class instead of struct?
Value types for data, reference types for behaviour.
For your FieldItem class, couldn't you create a property where you can inject a Predicate delegate method, such that:
class FieldItem
{
public string ObjectName {get; set;}
public string ObjectProperty {get; set;}
public string ObjectValue {get; set;}
public Predicate<FieldItem> EvalMethod { private get; set; }
public FieldItem()
{
}
public bool Evaluate()
{
return EvalMethod(this);
}
}
For constructing your FieldItem object then, you could use the following:
new FieldItem { ObjectName = "SomeObject",
ObjectProperty = "SomeProperty",
ObjectValue = "SomeValue",
EvalMethod = GetEvalMethodFor("SomeObject")
}
Of course you will have to have a parser of some sort (GetEvalMethodFor method above) to translate your validationRule string into a boolean evaluation method.
In your collection, you can then call the Evaluate() within, say, a foreach loop.
UPDATE
If you need to evaluate each and every object in the list, you can do this:
bool areAllItemsValid = true;
foreach (var f in fieldItemList)
{
areAllItemsValid = areAllItemsValid && f.Evaluate();
if (areAllItemsValid)
{
continue;
}
else
{
return false;
}
}
return true;
You can opt to inherit List<FieldItem> and make an EvaluateAll() method to contain the above, or declare it as a method in a separate object altogether.

Categories