Setting property values dynamically - c#

I am using a PivotGrid(DevExpress). I want to set AppearancePrint property settings in a for loop.
How do i use the variable type for properties such as Cell in the example below?
so instead of
grid.AppearancePrint.Cell.BackColor = Color.White;
grid.AppearancePrint.Cell.BackColor2 = Color.LightBlue;
I want to do this:
//datarow example <PrintAppearance Type="Cell" Font="Tahoma,8,Regular" BackColor="White" BackColor2="Light Grey"/>
foreach (DataRow dr in appearances)
{
string type = dr["Type"].ToString();
grid.AppearancePrint.[type].BackColor = Color.FromName(dr["BackColor"].ToString());
grid.AppearancePrint.[type].BackColor2 = Color.FromName(dr["BackColor2"].ToString());
}

This is essentially a form of script-parsing, and you'll need to use reflection in order to do it. For example:
foreach (DataRow dr in appearances) {
string type = dr["Type"].ToString();
PropertyInfo propertyForType = grid.AppearancePrint.GetType().GetProperty(type);
object objectForProperty = propertyForType.GetValue(grid.AppearancePrint, null);
PropertyInfo propertyForBackColor = objectForProperty.GetType().GetProperty("BackColor");
PropertyInfo propertyForBackColor2 = objectForProperty.GetType().GetProperty("BackColor2");
propertyForBackColor.SetValue(objectForProperty, Color.FromName(dr["BackColor"].ToString()), null);
propertyForBackColor2.SetValue(objectForProperty, Color.FromName(dr["BackColor2"].ToString()), null);
}

I'm not familiar with your exact problem but at a glance, it seems you'll need to use reflection as you won't know the type until runtime - In case you're not familiar with reflection, it will allow you to examine the object (and more importantly the properties on it)
See here for a possible solution

Related

Parameter count mismatch when getting value of property using Reflection

I am getting a Parameter Count mismatch error which I don't understand.
I have the below code:
Type target = Type.GetType("CPS_Service." + DocumentType);
// Create an instance of my target class
instance = Activator.CreateInstance(target);
foreach (XElement pQ in PQData.Elements())
{
try
{
// populate the member in the instance of the data class with the value from the MQ String
if (target.GetProperty(pQ.Attribute("name").Value) != null)
{
target.GetProperty(pQ.Attribute("name").Value).SetValue(instance, pqRequest[Convert.ToInt32(pQ.Attribute("pos").Value)], null);
}
}
}
PropertyInfo[] properties = target.GetProperties();
foreach (PropertyInfo property in properties)
{
DataColumn col = new DataColumn(property.Name);
col.DataType = System.Type.GetType("System.String");
col.DefaultValue = "";
dt.Columns.Add(col);
}
DataRow dr = dt.NewRow();
foreach (PropertyInfo property in properties)
{
string value = property.GetValue(instance).ToString();
dr[property.Name.ToString()] = "";
}
dt.Rows.Add(dr);
return dt; //
so I am instantiating a generic class and populating it from a string array (taken from a tab-delimited string), then I need to either output a List or a datatable from the class instance
When populating the datarow dr for my datatable dt I am trying to get the value from the class:
string value = property.GetValue(instance, null).ToString();
dr[property.Name.ToString()] = "";
but on the line property.GetValue(instance).ToString(); I get the below error:
Parameter count mismatch
I have searched around and the other questions about this error do not apply...
Or would I be better off just casting my class to a List and returning that?
If you're trying to get the value of all properties of a String (or any type that has an indexer), then you're going to have to have a special case to deal with the indexer. So if you wanted to get the value of that parameter you'd have to pass the object array of values with one parameter as the index value you wanted to get.
For example, property.GetValue(test, new object [] { 0 }); would get the value of the string at index 0. So if the string's value was "ABC", the result of would be 'A'.
The easiest thing would be just to skip the indexer. You can test if a property is an indexer by using property.GetIndexParameters().Any(). I thought you could skip this check by using the appropriate binding flag when calling GetProperties(), but if you can, I didn't see it.
If you want to skip the index in your code, then change:
PropertyInfo[] properties = target.GetProperties();
To:
var properties = target.GetProperties().Where(p => !p.GetIndexParameters().Any());

LINQ Deffered Execution Subtlety

All, I have recently localised the entire aplication and I am faced with the following problem;I have the following LINQ query in my application
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(strpcPSPassBegCost)) by new
{
BaseCC = a.Field<string>(strpcBaseCC)
}
into g
select new
{
g.Key.BaseCC,
PSPassBegCost = g.Sum()
};
This is creating a new object ccsum which we use to create a DataTable and subsequently populate an SQL Server database.
The problem is that each of the new items being created in the DataTable with column names BaseCC and PSPassBegCost, but these names do not matched the German version of these names. Now for my question: is there a way to do something like:
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(strpcPSPassBegCost)) by new
{
BaseCC = a.Field<string>(strpcBaseCC)
}
into g
select new
{
g.Key.BaseCC as Resources.BaseCC,
PSPassBegCost = g.Sum() as Resources.PSPassBegCost
};
so that I can name the tables according to their localised names?
Edit. The code that retrieve a DataTable from ccsum is
fooDt = Utils.LINQToDataTable(ccsum);
and
public static DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
PropertyInfo[] oProps = null;
if (varlist == null)
return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create
// table, Only first time, others will follow.
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
Thanks for your time.
Another approach might be to rename the 'columns' in the ccsum object as a post-processing step although the values of sumcc are not populated until they are requested at run-time - any other ideas?
Create class or enum which will map column indexes to some readable names:
public static class SumGLColumn
{
public const int BaseCC = 0;
public const int PSPassBegCost = 1;
}
And use column indexes instead of column names to query your datatable:
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(SumGLColumn.PSPassBegCost))
by a.Field<string>(SumGLColumn.BaseCC) into g
select new {
BaseCCg = g.Key,
PSPassBegCost = g.Sum()
};
Attempting to localize components of your system for greater human comprehension is a laudable goal, but it will give you challenges in using the the vast majority of tools and libraries: generally database tooling expects these things to be constant and ignorant of localization.
If you wish your database tables to be easier to understand, perhaps a more practical solution would be to produce localized views instead? The views could live in a de schema and be one-to-one translations of your tables. This would allow you to leverage a lot of the standard tooling, keeping your system in a consistent "neutral" culture internally (whatever your development culture is) and providing translations over the top of these wherever required.
I think trying to embed this kind of localization into the heart of your system is likely to not be worth the cost of working around the expectations of most developers and toolsets and you're better providing a façade.
This is not possible. In the select statement you define an anonymous type. This is not a language feature, but a compiler-feature, which means that the compiler creates a class for this type with the properties you define.
This means that the compiler must know the names at compile time. If you want something more dynamic, I recommend you to use a dictionary:
select new Dictionary<string, object>
{
{ Resources.BaseCC, g.Key.BaseCC },
{ Resources.PSPassBegCost , g.Sum() }
};

Get value of c# dynamic property via string

I'd like to access the value of a dynamic c# property with a string:
dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };
How can I get the value of d.value2 ("random") if I only have "value2" as a string? In javascript, I could do d["value2"] to access the value ("random"), but I'm not sure how to do this with c# and reflection. The closest I've come is this:
d.GetType().GetProperty("value2") ... but I don't know how to get the actual value from that.
As always, thanks for your help!
Once you have your PropertyInfo (from GetProperty), you need to call GetValue and pass in the instance that you want to get the value from. In your case:
d.GetType().GetProperty("value2").GetValue(d, null);
public static object GetProperty(object target, string name)
{
var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
return site.Target(site, target);
}
Add reference to Microsoft.CSharp. Works also for dynamic types and private properties and fields.
Edit: While this approach works, there is almost 20× faster method from the Microsoft.VisualBasic.dll assembly:
public static object GetProperty(object target, string name)
{
return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
Dynamitey is an open source .net std library, that let's you call it like the dynamic keyword, but using the a string for the property name rather than the compiler doing it for you, and it ends up being equal to reflection speedwise (which is not nearly as fast as using the dynamic keyword, but this is due to the extra overhead of caching dynamically, where the compiler caches statically).
Dynamic.InvokeGet(d,"value2");
The easiest method for obtaining both a setter and a getter for a property which works for any type including dynamic and ExpandoObject is to use FastMember which also happens to be the fastest method around (it uses Emit).
You can either get a TypeAccessor based on a given type or an ObjectAccessor based of an instance of a given type.
Example:
var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");
var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");
dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");
var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");
typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");
typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");
Much of the time when you ask for a dynamic object, you get an ExpandoObject (not in the question's anonymous-but-statically-typed example above, but you mention JavaScript and my chosen JSON parser JsonFx, for one, generates ExpandoObjects).
If your dynamic is in fact an ExpandoObject, you can avoid reflection by casting it to IDictionary, as described at http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx.
Once you've cast to IDictionary, you have access to useful methods like .Item and .ContainsKey
The GetProperty/GetValue does not work for Json data, it always generate a null exception, however, you may try this approach:
Serialize your object using JsonConvert:
var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));
Then access it directly casting it back to string:
var pn = (string)z["DynamicFieldName"];
It may work straight applying the Convert.ToString(request)["DynamicFieldName"], however I haven't tested.
d.GetType().GetProperty("value2")
returns a PropertyInfo object.
So then do
propertyInfo.GetValue(d)
To get properties from dynamic doc
when .GetType() returns null, try this:
var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
This is the way i ve got the value of a property value of a dinamic:
public dynamic Post(dynamic value)
{
try
{
if (value != null)
{
var valorCampos = "";
foreach (Newtonsoft.Json.Linq.JProperty item in value)
{
if (item.Name == "valorCampo")//property name
valorCampos = item.Value.ToString();
}
}
}
catch (Exception ex)
{
}
}
Some of the solutions were not working with a valuekind object that I obtained from a json string, maybe because I did not have a concrete type in my code that was similar to the object that I would obtain from the json string, so how I went about it was
JsonElement myObject = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(jsonStringRepresentationOfMyObject);
/*In this case I know that there is a property with
the name Code, otherwise use TryGetProperty. This will
still return a JsonElement*/
JsonElement propertyCode = myObject.GetProperty("Code");
/*Now with the JsonElement that represents the property,
you can use several methods to retrieve the actual value,
in this case I know that the value in the property is a string,
so I use the GetString method on the object. If I knew the value
was a double, then I would use the GetDouble() method on the object*/
string code = propertyCode.GetString();
That worked for me
In .Net core 3.1 you can try like this
d?.value2 , d?.value3
Similar to the accepted answer, you can also try GetField instead of GetProperty.
d.GetType().GetField("value2").GetValue(d);
Depending on how the actual Type was implemented, this may work when GetProperty() doesn't and can even be faster.
In case you have a dynamic variable such as a DapperRow for example you can first build up an ExpandoObject, then cast the Expando into an IDictionary<string, object>. From then on, getting a value via the name of a property is possible.
Helper method ToExpandoObject:
public static ExpandoObject ToExpandoObject(object value)
{
IDictionary<string, object> dapperRowProperties = value as IDictionary<string, object>;
IDictionary<string, object> expando = new ExpandoObject();
if (dapperRowProperties == null)
{
return expando as ExpandoObject;
}
foreach (KeyValuePair<string, object> property in dapperRowProperties)
{
if (!expando.ContainsKey(property.Key))
{
expando.Add(property.Key, property.Value);
}
else
{
//prefix the colliding key with a random guid suffixed
expando.Add(property.Key + Guid.NewGuid().ToString("N"), property.Value);
}
}
return expando as ExpandoObject;
}
Sample usage, I have marked in bold the casting which gives us access in the example, I have marked the important bits with the ** letters:
using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
foreach (var dynamicParametersForItem in dynamicParametersForItems)
{
var idsAfterInsertion = (await connection.QueryAsync<object>(sql, dynamicParametersForItem)).ToList();
if (idsAfterInsertion != null && idsAfterInsertion.Any())
{
**var idAfterInsertionDict = (IDictionary<string, object>) ToExpandoObject(idsAfterInsertion.First());**
string firstColumnKey = columnKeys.Select(c => c.Key).First();
**object idAfterInsertionValue = idAfterInsertionDict[firstColumnKey];**
addedIds.Add(idAfterInsertionValue); //we do not support compound keys, only items with one key column. Perhaps later versions will return multiple ids per inserted row for compound keys, this must be tested.
}
}
}
In my example, I look up a property value inside a dynamic object DapperRow and first convert the Dapper row into an ExpandoObject and cast it into a dictionary property bag as shown and mentioned in other answers here.
My sample code is the InsertMany method for Dapper extension I am working on, I wanted to grab hold of the multiple ids here after the batch insert.
Use dynamic with Newtonsoft.Json.JsonConvert.DeserializeObject:
// Get JSON string of object
var obj = new { value1 = "some", value2 = "random", value3 = "value" };
var jsonString = JsonConvert.SerializeObject(obj);
// Use dynamic with JsonConvert.DeserializeObject
dynamic d = JsonConvert.DeserializeObject(jsonString);
// output = "some"
Console.WriteLine(d["value1"]);
Sample:
https://dotnetfiddle.net/XGBLU1

How to iterate over numerically named object properties

So I have a horribly designed class that I can't change that has properties like this:
object.Color1
object.Color2
object.Color3
etc...
How can I iterate through those with a for loop. In other words, something like this:
for (int i = 0; i <= 40; i++)
{
string PropertyName = "Color" + i;
if (object.PropertyName != "")
{
// do something
}
}
Obviously this code wouldn't work but it gives you an idea of what I'm after. I have to do some processing on each property and I don't want to repeat my code 40 times. :) A loop would be perfect, I'm just not sure how to create the name of the property on the fly.
EDIT: Ok so I've tried the following code:
for (int i = 1; i <= 20; i++ )
{
var type = pendingProduct.GetType();
var colorProperty = type.GetProperty("Color" + i);
string colorValue = colorProperty.GetValue(type, null).ToString();
var colorSkuProperty = type.GetProperty("Color" + i + "SKU");
string colorSkuValue = colorSkuProperty.GetValue(type, null).ToString();
if (String.IsNullOrEmpty(colorValue)) continue;
ProductColor color = new ProductColor {Color = colorValue, ProductSizes = productSizes};
if (!String.IsNullOrEmpty(colorSkuValue)) color.SKU = colorSkuValue;
}
I'm getting an error "Object does not match target type" on this line:
string colorValue = colorProperty.GetValue(type, null).ToString();
Am I doing something wrong here?
You're looking for reflection:
PropertyInfo property = typeof(SomeType).GetProperty("Color" + i);
string value = (string)property.GetValue(obj, null);
Note that this will be slow.
If you do it many times, you can make it faster by caching Delegate.CreateDelegate(..., property.GetGetMethod()) in an array.
You can use reflection for that:
var type = myObject.GetType();
var property = type.GetProperty("Color1");
var value = property.GetValue(myObject, null));
This is how i do to help finding.
$abc = array(
'a' => 'world',
'b' => 'good',
);
$object = (object) $abc;
foreach($object as $k)
{
var_dump($object);
}
You can use reflection to get the property by name. In your example you can use:
var pi = obj.GetType().GetProperty(PropertyName);
var val = pi.GetValue(obj,null);
In order to obtain the value of the property which name is PropertyName. You should check for pi != null because if a requested property does not exists null is returned.
If the function you are writing is time critical, you should anyway pay attention that reflection has some performance drawbacks.
Have a look at InvokeMethod...
MSDN
Example at codeproject
You could try something along the lines of...
Type type = this.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo p in properties)
{
// Check property is the one you want
// and carry out your code...
}
Also, If you have access to the code I.E. you can change the internals but want to leave the public API intact you could just add the information to a private collection when the object is constructed. The collection could then be exposed by a public property for you to iterate over. A bit hacky but an alternative to using reflection.

Is it possible to get an 'object' from a PropertyInfo?

In my precent questions, I want to retrieve some values via reflection.
Now I want set values to objects thanks to reflection.
I want to write this :
private void AppliquerColonnesPersonnalisation(Control control, Propriete propriete, PropertyInfo Info)
{
UltraGrid grille = (UltraGrid)control;
SortedList<int,string> sortedOrderedColumns = new SortedList<int,string>();
if (grille != null)
{
// I want to write MapPropertyInfo method
ColumnsCollection cols = MapPropertyInfo(Info);
PropertyInfo contains a type of ColumnsCollection. I just want to "map" my PropertyInfo to an object to define some properties after : For example :
cols[prop.Nom].Hidden = false;
Is it possible ?
Best Regards,
Florian
EDIT : I tried the GenericTypeTea solution, but I have some problem. Here my code snippet :
private void AppliquerColonnesPersonnalisation(Control control, Propriete propriete, PropertyInfo Info)
{
UltraGrid grille = (UltraGrid)control;
ColumnsCollection c = grille.DisplayLayout.Bands[0].Columns;
// Throw a not match System.Reflection.TargetException
ColumnsCollection test = Info.GetValue(c,null) as ColumnsCollection;
SortedList<int,string> sortedOrderedColumns = new SortedList<int,string>();
But a TargetException is Thrown
So you already have a PropertyInfo object that is of type ColumnsCollection?
You can get it and modify it using the following code:
var original = GetYourObject();
PropertyInfo Info = GetYourPropertyInfo(original);
ColumnsCollection collection = Info.GetValue(original) as ColumnsCollection;
Basically, you just need to pass your original object back into the PropertyInfo's GetValue method which will return you an object. Just cast that as the ColumnsCollection and you should be sorted.
UPDATE:
Based on your update, you should be doing this:
object original = grille.DisplayLayout.Bands[0];
PropertyInfo info = original.GetProperty("Columns");
ColumnsCollection test = info.GetValue(original, null) as ColumnsCollection;
You must be getting your Info PropertyInfo from an object of a different type. Although I think we're fixing the wrong problem here. I don't understand what you're trying to achieve. Why not just modify grille.DisplayLayout.Bands[0].Columns directly?

Categories

Resources