How to dynamically set a property of a class without using reflection (with dynamic) in C# 4 when property name is coming from another source - reflection

I'm building/updating an EntityFramework EntityObject on runtime. I want to set the properties of the entity class, property names and values are coming from another source.
So I'm doing this;
public static EntityCollection<T> UpdateLocaleEntity<T>(EntityCollection<T> entityCollectionToUpdate, params ILocaleControl[] values) where T : EntityObject
{
foreach (var x in entityCollectionToUpdate)
{
Type t = typeof(T);
dynamic localeEntity = x;
string cultureCode = localeEntity.CultureCode;
for (int j = 0; j < values.Length; j++)
{
var value = values[j].GetLocaleValue(cultureCode);
t.GetProperty(values[j].EntityPropertyName).SetValue(localeEntity, value, null);
}
}
return entityCollectionToUpdate;
}
So, how can I get rid of "t.GetProperty(values[j].EntityPropertyName).SetValue(localeEntity, value, null);" part, is there a dynamic way of doing this?
Something like;
dynamicCastedLocaleEntity.GetProperty(values[j].EntityPropertyName) = value;
Thanks.

Long answer coming up.
Reflection is great in many situations, horrible in some but in almost all cases it's slow.
There are at least 4 different ways to set a property in .NET without having to use reflection.
I thought I demonstrate one of them: Using compiled expression trees. Note that the expression building is rather expensive too so that's why it's very important to cache the delegate one builds with it in a dictionary (for instance):
Expression Trees was introduced in .NET35 and is used for many things. Here I use them to build a property setter expression and then compile it into a delegate.
The example demonstrates different timing for the different cases but here are my numbers:
Control case (hard coded): 0.02s
Reflection: 1.78s
Expression Tree: 0.06s
using System;
using System.Linq.Expressions;
namespace DifferentPropertSetterStrategies
{
class TestClass
{
public string XY
{
get;
set;
}
}
class DelegateFactory
{
public static Action<object, object> GenerateSetPropertyActionForControl(
)
{
return (inst, val) => ((TestClass) inst).XY = (string) val;
}
public static Action<object, object> GenerateSetPropertyActionWithReflection(
Type type,
string property
)
{
var propertyInfo = type.GetProperty(property);
return (inst, val) => propertyInfo.SetValue (inst, val, null);
}
public static Action<object,object> GenerateSetPropertyActionWithLinqExpression (
Type type,
string property
)
{
var propertyInfo = type.GetProperty(property);
var propertyType = propertyInfo.PropertyType;
var instanceParameter = Expression.Parameter(typeof(object), "instance");
var valueParameter = Expression.Parameter(typeof(object), "value");
var lambda = Expression.Lambda<Action<object, object>> (
Expression.Assign (
Expression.Property (Expression.Convert (instanceParameter, type), propertyInfo),
Expression.Convert(valueParameter, propertyType)),
instanceParameter,
valueParameter
);
return lambda.Compile();
}
}
static class Program
{
static void Time (
string tag,
object instance,
object value,
Action<object, object > action
)
{
// Cold run
action(instance, value);
var then = DateTime.Now;
const int Count = 2000000;
for (var iter = 0; iter < Count; ++iter)
{
action (instance, value);
}
var diff = DateTime.Now - then;
Console.WriteLine ("{0} {1} times - {2:0.00}s", tag, Count, diff.TotalSeconds);
}
static void Main(string[] args)
{
var instance = new TestClass ();
var instanceType = instance.GetType ();
const string TestProperty = "XY";
const string TestValue = "Test";
// Control case which just uses a hard coded delegate
Time(
"Control",
instance,
TestValue,
DelegateFactory.GenerateSetPropertyActionForControl ()
);
Time(
"Reflection",
instance,
TestValue,
DelegateFactory.GenerateSetPropertyActionWithReflection (instanceType, TestProperty)
);
Time(
"Expression Trees",
instance,
TestValue,
DelegateFactory.GenerateSetPropertyActionWithLinqExpression(instanceType, TestProperty)
);
Console.ReadKey();
}
}
}

For FuleSnabel's answer, you can speed it up a lot (sometimes twice as fast in my tests). In some tests, it was just as fast as the Control solution:
public static Action<Object,Object> GenerateSetPropertyActionWithLinqExpression2(Type type, String property) {
PropertyInfo pi = type.GetProperty(property,BindingFlags.Instance|BindingFlags.Public);
MethodInfo mi = pi.GetSetMethod();
Type propertyType = pi.PropertyType;
var instance = Expression.Parameter(typeof(Object), "instance");
var value = Expression.Parameter(typeof(Object), "value");
var instance2 = Expression.Convert(instance, type);
var value2 = Expression.Convert(value, pi.PropertyType);
var callExpr = Expression.Call(instance2, mi, value2);
return Expression.Lambda<Action<Object,Object>>(callExpr, instance, value).Compile();
}

possibly not with EntityObject, but if you've had an ExpandoObject than you can do
dynamic entity = new ExpandoObject();
(entity as IDictionary<String, Object>)[values[j].EntityPropertyName] = value

The open source framework ImpromptuInterface has methods to invoke based on a string using the DLR rather than reflection and runs faster than reflection too.
Impromptu.InvokeSet(localeEntity, values[j].EntityPropertyName,value);

I'm afraid not. Any use of a dynamic object is baked-in at compile time. Any call which could vary at run-time has to be done using reflection.

Related

How to retrieve attributes from a classDeclaration with reflection

I want to create a custom attribute that will be applied to classDeclarations. I can enumerate attributes from other methods on the class, but not the classDeclaration itself because it is some sort of special method.
I know it is possible though because SysObsoleteAttribute (called from the kernel) is placed in classDeclarations all over.
In Classes\CustCustomerService\create I just copied the attributes to Classes\CustCustomerService\classDeclaration at the top for this test.
[AifDocumentCreateAttribute, SysEntryPointAttribute(true)]
class CustCustomerService extends AifDocumentService
{
}
I created a static method on a new class:
static public void AttribsOfSysEntryPointAttributeOnMethod
(
str _sNameOfClass,
str _sNameOfMethod,
str _nameOfAttribute
)
{
int nClassId;
SysDictMethod sdm;
Object attributeAsObject;
SysDictClass sysDictClass;
Array attribArray = new Array(Types::Class);
int i;
nClassId = Global::className2Id(_sNameOfClass);
sysDictClass = new SysDictClass(nClassId);
sdm = new SysDictMethod(UtilElementType::ClassInstanceMethod, nClassId, _sNameOfMethod);
attribArray = sdm.getAllAttributes();
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
{
// Unable to get attributes, try another way
error(strFmt("Unable to retrieve attribute array for method %1", sdm.name()));
// It is, so let's try and enumerate ALL attributes and output them directly from class dec
sdm = sysDictClass.objectMethodObject(1);
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
error(strFmt("Still unable to retrieve attribute array for method %1", sysDictClass.objectMethod(1)));
}
}
Then created a job to call it, and you can see how it works for one method, but not the other.
static void Job5(Args _args)
{
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "create", "SysEntryPointAttribute");
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "classDeclaration", "SysEntryPointAttribute");
}
Any ideas how to enumerate Attributes from the classDeclaration??
The classDeclaration is not a method and cannot be called. Hence your sysDictClass variable is null.
Googling reveals that the getAllAttributes method exits on DictClass:
attribArray = sdm ? sdm.getAllAttributes() : sysDictClass.getAllAttributes();

LINQ dynamic property in select

// Hi everyone
i do this call in Action :
[HttpGet]
public virtual ActionResult JsonGetProvinces(int countryId)
{
//WebSiteContext WbContext = new WebSiteContext();
//UnitOfWork UnitofWork = new UnitOfWork(WbContext);
var provinces =
(
from province in unitofWork.ProvinceRepository.All
where province.CountryId == countryId
select new
{
Id = province.Id,
Name = province.GetType().GetProperty("Name_" + CultureManager.GetCurrentCultureShortName()).GetValue(province)
}
).ToList();
return Json(provinces, JsonRequestBehavior.AllowGet);
}
something is wrong with my query :
var provinces =
(
from province in unitofWork.ProvinceRepository.All
where province.CountryId == countryId
select new
{
Id = province.Id,
Name = province.GetType().GetProperty("Name_" + CultureManager.GetCurrentCultureShortName()).GetValue(province)
}
).ToList();
Particulary,
Name = province.GetType().GetProperty("Name_" + CultureManager.GetCurrentCultureShortName()).GetValue(province)
In BDD, there is Name_fr, Name_en columns
and i'm trying to take one dynamically... Is it possible ?
Of course, i can take both and choose dynamically the column in View but i would to know how do...
Thank you for your help
The short answer is you need to change your code a bit and using expression tree inside. Look at this question
EF can not translate function calls to SQL. Using expression trees can be comlicated see this question
Here is a sample with expression trees. The GetQuery2 is the same as GetQuery but with expression tree and a propertyname parameter.
public static IQueryable<Foo> GetQuery(BlogContext context)
{
var query = from x in context.BlogEntries
select new Foo
{
NameX = x.Name
};
return query;
}
public static IQueryable<Foo> GetQuery2(BlogContext context, string propertyName)
{
ConstructorInfo ci = typeof(Foo).GetConstructor(new Type[0]);
MethodInfo miFooGetName = typeof(Foo).GetMethod("set_NameX");
MethodInfo miBlogEntry = typeof(BlogEntry).GetMethod("get_" + propertyName);
ParameterExpression param = Expression.Parameter(typeof(BlogEntry), "x");
IQueryable<Foo> result = Queryable.Select<BlogEntry, Foo>(
context.BlogEntries,
Expression.Lambda<Func<BlogEntry, Foo>>(
Expression.MemberInit(
Expression.New(ci, new Expression[0]),
new MemberBinding[]{
Expression.Bind(miFooGetName,
Expression.Property(param,
miBlogEntry))}
),
param
)
);
return result;
}
It is easier the fetch all all language strings and write an additional Property Name that does the magic.

Retrieve Arguments of a Workflow (with default values)?

Given is a Workflow Foundation 4 runtime that is working against a website ;)
We need to get the arguments of workflows to show the user an editor to enter the arguments. For that we need all arguments with names, types and - default values, as well as an indication whether an argument is required.
Workflows are stored as XAML files.
How to do that? The data seems to be in the Activity Metadata which seems to be not avaialble outside the Workflow. In addition, the Workflow Engine ModelService is for the Designer and has a lot of overhead.
Any easy way to retrieve this information?
I've already done something similar. Reflection might be your best (and only) option if you want a generic approach.
// Just an holder for InArgument informations
class InArgumentInfo
{
public string InArgumentName { get; set; }
public string InArgumentDescription { get; set; }
public bool InArgumentIsRequired { get; set; }
}
static ICollection<InArgumentInfo> GetInArgumentsInfos(Activity activity)
{
var properties = activity.GetType()
.GetProperties()
.Where(p => typeof(InArgument).IsAssignableFrom(p.PropertyType))
.ToList();
var argumentsCollection = new Collection<InArgumentInfo>();
foreach (var property in properties)
{
var descAttribute = property
.GetCustomAttributes(false)
.OfType<DescriptionAttribute>()
.FirstOrDefault();
string description = descAttribute != null && !string.IsNullOrEmpty(descAttribute.Description) ?
descAttribute.Description :
string.Empty;
bool isRequired = property
.GetCustomAttributes(false)
.OfType<RequiredArgumentAttribute>()
.Any();
argumentsCollection.Add(new InArgumentInfo
{
InArgumentName = property.Name,
InArgumentDescription = description,
InArgumentIsRequired = isRequired
});
}
return argumentsCollection;
}
This way you can not only retrieve the argument's name but also other information hold by the argument's attributes. For example I choose to give argument an user-friendly name through [Description] attribute (eg. instead of MyPropertyName user sees "My Property Name").
Note: if you can ensure that you activity is an ActivityBuilder or DynamicActivity they both have Properties property that you can use, but the principle is the same.
Load it as DynamicActivity and iterate over Properties property
var dynamicActivity = ActivityXamlServices.Load(foo) as DynamicActivity
foreach(DynamicActivityProperty prop in dynamicActivity.Properties)
{
// ...
}
UPDATE: Missed default value part
foreach (var prop in dynamicActivity .Properties)
{
object defaultValue;
if (prop.Value == null)
{
defaultValue = null;
}
else
{
Type genericTypeDefinition = prop.Type.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(InArgument<>) || genericTypeDefinition == typeof(InOutArgument<>))
{
var valueProp = prop.Value.GetType().GetProperty("Expression", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.DeclaredOnly);
var expression = valueProp.GetValue(prop.Value, null);
var expressionValueProp = expression.GetType().GetProperty("Value");
defaultValue = expressionValueProp.GetValue(expression, null);
}
}
}
Not totally guaranteed, there are some checks you have to do.

Best way to typecast deserialised JSON

I think I've established that in as3corelib JSON.decode I have no choice but to deserialise to a plain old flex object.
var data:Object = JSON.decode(json);
If I then want to get the data contained in the object into another type I can't use type casting. I have to instantiate a new instance and add the properties manually.
var data:Object = JSON.decode(json);
var model:Model = new Model();
model.name = data.name;
model.notes = data.notes;
A pain and a bit ugly, but I'm guessing this is the price to be paid for going from untyped json to a flex type. My first question is whether my assumption is correct and there is no prettier way to create my model instance with the data contained within the json?
My second question, if so then before I write my own method to do this, is there anything inside the flex api that will take the data object and mixin it's values to my model instance?
Cheers,
Chris
the approach I've always used proved to be part of the AMF3 serialization mechanism in ActionScript.
have a look at IExternalizable and registerClassAlias.
now what I use is the following:
interface ISerializable {
public function getRawData():Object;
public function setRawData(param:Object):void;
}
function registerType(id:String, type:Class):void {
//implementation
}
function getTypeByID(id:String):Class {
//implementation
}
function getTypeID(type:Class):String {
//implementation
}
and to the decoder/encoder you register a class alias.
serialization of an object works as follows:
var raw:Object = model.getRawData();
raw[" type"] = getTypeID(model);
var encoded:String = JSON.encode(raw);
decoding works as follows:
var raw:Object = JSON.decode(raw);
var cl:Class = getTypeByID(raw[" type"]);
if (cl == null) throw new Error("no class registered for type: "+raw[" type"]);
delete raw[" type"];
var model:ISerializable = new cl();
model.setRawData(raw);
you will need to do this recursively on the whole deserialized JSON tree, starting at the leafs.
For cyclic reference, you'll need a trick.
I had an implementation of this somewhere, but I can't find it.
You can loop within the field of you json decoded object and assign them into your model:
function json2model(json:String):Model{
var data:Object = JSON.decode(json);
var m:Model=new Model();
for (var field:String in data) {
if (m.hasOwnProperty(field)) {
m[field] = data[field];
}
}
return m;
}
var model:Model=json2model(json)
or add a static function within your Model if you preffer:
public class Model {
//...
public static function fromJSon(json:String):Model {
var data:Object = JSON.decode(json);
var m:Model=new Model();
for (var field:String in data) {
if (m.hasOwnProperty(field)) {
m[field] = data[field];
}
}
return m;
}
}
}
var model:Model=Model.fromJSon(json);

Using reflection to set a property of a property of an object

I've got two classes.
public class Class1 {
public string value {get;set;}
}
public class Class2 {
public Class1 myClass1Object {get;set;}
}
I've got an object of type Class2. I need to use reflection on Class2 to set the value property... i.e, if I were doing it without reflection, this is how I would go about it:
Class2 myObject = new Class2();
myObject.myClass1Object.value = "some value";
Is there a way to do the above, while using reflection to access the property "myClass1Object.value" ?
Thanks in advance.
Basically split it into two property accesses. First you get the myClass1Object property, then you set the value property on the result.
Obviously you'll need to take whatever format you've got the property name in and split it out - e.g. by dots. For example, this should do an arbitrary depth of properties:
public void SetProperty(object source, string property, object target)
{
string[] bits = property.Split('.');
for (int i=0; i < bits.Length - 1; i++)
{
PropertyInfo prop = source.GetType().GetProperty(bits[i]);
source = prop.GetValue(source, null);
}
PropertyInfo propertyToSet = source.GetType()
.GetProperty(bits[bits.Length-1]);
propertyToSet.SetValue(source, target, null);
}
Admittedly you'll probably want a bit more error checking than that :)
I was looking for answers to the case where to Get a property value, when the property name is given, but the nesting level of the property is not known.
Eg. if the input is "value" instead of providing a fully qualified property name like "myClass1Object.value".
Your answers inspired my recursive solution below:
public static object GetPropertyValue(object source, string property)
{
PropertyInfo prop = source.GetType().GetProperty(property);
if(prop == null)
{
foreach(PropertyInfo propertyMember in source.GetType().GetProperties())
{
object newSource = propertyMember.GetValue(source, null);
return GetPropertyValue(newSource, property);
}
}
else
{
return prop.GetValue(source,null);
}
return null;
}
public static object GetNestedPropertyValue(object source, string property)
{
PropertyInfo prop = null;
string[] props = property.Split('.');
for (int i = 0; i < props.Length; i++)
{
prop = source.GetType().GetProperty(props[i]);
source = prop.GetValue(source, null);
}
return source;
}

Resources