I have following situation. Lets say i receive list of string values. I have to assign those values to specific type properties in Model.
Model example:
public int ID { get; set; }
public DateTime? Date { get; set; }
There is no problem in type conversion as i get array of property types with System.reflection and use Convert.ChangeType(string value, type).
However, i can't assignt Convert.ChangeType results to model properties because it returns object not my desired type of value.
A short example of my problem:
string s1 = "1";
string s2= "11-JUN-2015";
PropertyInfo[] matDetailsProperties = Model.GetType().GetProperties();
List<Type> types = new List<Type>();
foreach(var item in Model)
{
types.Add(item.PropertyType);
}
Model.ID = Convert.ChangeType(s1, types[0]);
Model.Date = Convert.ChangeType(s2, types[1]);
This does not work as Convert.ChangeType returns object and i can't just use (dateTime)Convert.ChangeType(...), that's "dirty code" as i have model with 17 properties with different types. It would be perfect if i could use (Type[0])Convert.ChangeType(...) but it is not possible in C#
You could use reflection. How about something like this?
var prop = Model.GetType().GetProperty("ID");
var propValue = Convert.ChangeType(s1, types[0]);
if (prop != null && prop.CanWrite)
{
prop.SetValue(Model, propValue, null);
}
instead of Parse i'll suggest you use TryParse(string, out DateTime)
int tempId=default(int);
DateTime tempDate=DateTime.Min;
int.TryParse(s1,out tempId);
DateTime.TryParse(s2,out tempDate);
Model.ID = tempId;
Model.Date = tempDate;
You do not need to use Convert.ChangeType. Just use the existing parsers inside a function to create your model in a simple-to-follow fashion:
private static Model PopulateModel(IEnumerable<string> rawData)
{
return new Model
{
ID = int.Parse(rawData[0]),
Date = DateTime.Parse(rawData[1]),
...
};
}
Related
I am trying to find a way to take a class's property and pass it to a method along with another variable to update the property based on conditions. For example
The class
public class MyClass{
public string? Prop1 { get; set; }
public string? Prop2 { get; set; }
public bool? Prop3 { get; set; }
public DateTime? Prop4 { get; set; }
... etc...
}
Test code I would like to get to work...:
var obj = new MyClass();
MyCheckMethod(ref obj.Prop1, someCollection[0,1]);
in the method:
private void MyCheckMethod(ref Object obj, string value)
{
if (!string.isnullorempty(value))
{
// data conversion may be needed here depending on data type of the property
obj = value;
}
}
I want to be able to pass any property of any class and update the property only after validating the value passed in the method. I was hoping I could do this with generics, but I haven't yet found a way to do so. Or if I am over complicating what I need to do.
The problem is that there may be a bit more to the validation of the passed in value than just a simple isnullorempy check.
I also thought about doing something like this:
private void MyCheckMethod(ref object obj, Action action)
Then I could do something like this:
...
MyCheckMethod(ref obj.Prop1, (somecollection[0,1]) => {
... etc....
})
So I am looking for some guidance on how to proceed.
updated info:
The incoming data is all in string format (this is how a 3rd party vendor supplies the data). The data is supplied via API call for the 3rd party product... part of their SDK. However in my class I need to have proper data types. Convert string values to datetime for dates, string values to int for int data types, etc... . The other caveat is that if there isnt a valid value for the data type then the default value of the property should be NULL.
Additional Information:
The incoming data is always in string format.
eg:
I have to update a boolean property.
The incoming value is "". I test to see if the string Value isNullOrEmpty. It is so I dont do anything to property.
The next property datatype is decimal.
The incoming value is "0.343".
I Test to see if the string value is NullorEmpty. It isnt so I can update the property once I do a convert etc.....
Hope this helps.
Thanks
Full solution after edits:
public static class Extensions
{
//create other overloads
public static void MyCheckMethodDate<TObj>(this TObj obj,Expression<Func<TObj,DateTime>> property, string value)
{
obj.MyCheckMethod(property, value, DateTime.Parse);
}
public static void MyCheckMethod<TObj,TProp>(this TObj obj,Expression<Func<TObj,TProp>> property, string value,Func<string, TProp> converter)
{
if(string.IsNullOrEmpty(value))
return;
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if(null != propertyInfo && propertyInfo.CanWrite)
{
propertyInfo.SetValue(obj, converter(value));
}
}
}
public class Obj
{
public object Prop1{get;set;}
public string Prop2{get;set;}
public DateTime Prop3{get;set;}
}
public class Program
{
public static void Main()
{
var obj = new Obj();
obj.MyCheckMethodDate(x=>x.Prop3, "2018-1-1");
Console.WriteLine(obj.Prop3);
}
}
You can pass a lambda expression:
void DoSomething<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}else{
var name = ((MemberExpression)property.Body).Member.Name;
var value = property.Compile();
//Do whatever you need to do
}
}
To use:
DoSomething(() => obj.Property1);
You can't pass a reference to an arbitrary property. A property is basically implemented as two methods, set_Property and get_Property, and there's no way to bundle these together.
One option is to have your checker function take delegates to access the property. For example:
private void MyCheckMethod(Func<string> getter, Action<string> setter)
{
var value = getter();
var newValue = value.ToUpper();
setter(value);
}
So now you would say something like this:
public class MyClass
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
var c = new MyClass();
MyCheckMethod(() => c.Prop1, value => c.Prop1 = value);
Use reflection with compiled expressions.
This performs better than reflection and a little bit slower than native code.
It's not type safe, but you can add runtime validation.
I'm trying to build some objects based on properties coming from another object. The class of the objects I need to build is
public class Data
{
public string Attribute { get; set; }
public string Value{ get; set; }
}
And the attribute will be the name of the property (and the value its value)
So I was trying to use Expressions trees to make a method that I can use for avoiding hard coding that attribute
Up to the moment I came to these couple of methods, based on a couple of posts I was reading on the net
public static string GetName<T>(Expression<Func<T>> e)
{
var member = (MemberExpression)e.Body;
return member.Member.Name;
}
public static Data BuildData<T>(Expression<Func<T>> e, appDetailCategory category)
{
var member = (MemberExpression)e.Body;
Expression strExpr = member.Expression;
var name = member.Member.Name;
var value = Expression.Lambda<Func<string>>(strExpr).Compile()();
return new Data
{
Attribute = name,
Value = value
};
}
But the line I'm trying to set the value raises an exception:
Expression of type 'AutomapperTest.Program+DecisionRequest' cannot be used for return type 'System.String'
I'm pretty sure this message it's supposed to make the error obvious but it's not for me
UPDATE:
I'm calling it this way
private static Data[] GetApplicatonDetailsFromRequest(DecisionRequest request)
{
BuildData(() => request.PubID)
//...
}
Must be member, not member.Expression.
public static Data BuildData<T>(Expression<Func<T>> e, appDetailCategory category)
{
var member = (MemberExpression)e.Body;
var name = member.Member.Name;
var value = Expression.Lambda<Func<string>>(member).Compile()();
return new Data
{
Attribute = name,
Value = value
};
}
It looks like your problem is that the type of PubID isn't a string. You've got two options, either change Data to store the value as an object or call ToString on the value returned from the property and store it. For example:
public static Data BuildData<T>(Expression<Func<T>> e)
{
var member = (MemberExpression)e.Body;
var name = member.Member.Name;
Func<T> getPropertyValue=e.Compile();
object value = getPropertyValue();
return new Data
{
Attribute = name,
Value = value.ToString()
};
}
Note that to get the value you can just compile the Func expression.
I need to iterate through the properties of an object and obtain the values of each primitive property (and strings). I already have code in place to deal with indexers, complex objects and nullable primitives, so don't worry about those (I excluded those from the code to keep it simple). The problem I'm running into is when I use Convert.ChangeType(). The variable "value" is always of type System.Object. How can I convert it to a string or int as shown in the "SomeObject" object?
SomeObject someObject = new SomeObject();
foreach (PropertyInfo propertyInfo in someObject.GetType().GetProperties())
{
Type propertyType = propertyInfo.PropertyType;
// How can I cast this to the type of "propertyType"?
var value = Convert.ChangeType(propertyInfo.GetValue(someObject, null), propertyType);
}
public class SomeObject
{
public string SomeString { get; set; }
public int SomeInt { get; set; }
}
You can do something like
if (propertyType == typeof(int))
{
var value = (int)Convert.ChangeType(propertyInfo.GetValue(testo, null), propertyType);
}
else if(propertyType == typeof(string))
{
var value = (string)Convert.ChangeType(propertyInfo.GetValue(testo, null), propertyType);
}
I have a List that I am iterating through.
Inside the List<> are Argument classes which contain two properties 'PropertyName' and 'Value'
What I need to do is iterate through the collection of Arguments and assign the Value of that Argument to the Property (with the same name as current Argument) of a different class.
Example:
Argument:
PropertyName: ClientID
Value: 1234
Members Class:
ClientID = {Argument Value here}
I hope this makes sense. I have a way of doing it, hard coding the properties of my class and matching it up with the Argument list.
Something like:
foreach(var arg in List<Argument>)
{
Members.ClientID = arg.Find(x => compareName(x, "ClientID")).Value;
//where compareName just does a simple string.Compare
}
But what would the BEST way be for something like this?
EDIT: Sorry about this guys and thanks for the replies so far. Here is what I didn't mention and might make a difference.
Each argument is a different property for the same class. I am iterating through the List and each one in there will be for the same Members class I have to populate.
I wanted to mention this because im thinking in the foreach I might have to use a switch to determine what 'PropertyName' I have for that Argument. ClientID is one of them but I believe there are 14 total properties in the Members class that need populated from the Collection.
Does that change things?
Thanks again
public object this[string propertyName]
{
get
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
Then you can get/set properties within the class using
myClassInstance["ClientId"] = myValue;
If I understand what you're asking, perhaps something like this will work for you:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value);
Members.ClientID = argDict["ClientID"];
...
If you need to do some special comparison on the keys you can provide the dictionary it's own IEqualityComparer. For example, this will make sure that the keys are treated case-insensitively:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value,
StringComparer.OrdinalIgnoreCase);
This will work fine as long as the arguments list contains all the values you need. If some arguments might be missing, you'd have to do something like this:
if (argDict.ContainsKey("ClientID")) {
Members.ClientID = argDict["ClientID"];
}
Or possibly something like this:
Members.ClientID = argDict.ContainsKey("ClientID") ? argDict["ClientID"] : "DefaultValue";
I think that your basic intent is to set the value of a property on a target object based on the property name. Since you did not provide the Argument class I will assume it is defined like this:
public class Argument
{
public string PropertyName{get; set;}
public object PropertyValue{get;set;}
}
Further assume you have the class Blah defined like this:
public class Blah
{
public string AString{get; set;}
public int AnInt{get; set;}
public DirectoryInfo ADirInfo{get; set;}
}
If you wish to assign to the properties of a Blah object based on the values in List<Argument> you can do so like this:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = new DirectoryInfo(#"c:\logs")}
};
Blah b = new Blah();
Type blahType = b.GetType();
foreach(Argument arg in arguments)
{
PropertyInfo prop = blahType.GetProperty(arg.PropertyName);
// If prop == null then GetProperty() couldn't find a property by that name. Either it doesn't exist, it's not public, or it's defined on a parent class
if(prop != null)
{
prop.SetValue(b, arg.PropertyValue);
}
}
This depends on the objects stored in Argument.PropertyValue having the same type as the property of Blah referred to by Argument.PropertyName (or there must be an implicit type conversion available). For example, if you alter the List<Argument> as follows:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = "foo"}
};
you will now get an exception when attempting to assign to Blah.ADirInfo: Object of type 'System.String' cannot be converted to type 'System.IO.DirectoryInfo'
Can I cast ExpandoObject to anonymous type ?
var anoObj = new { name = "testName", email = "testEmail" };
dynamic expandoObj = new System.Dynamic.ExpandoObject();
// Here I'm populating the expandoObj with same property names/types in anonymoustype(anoObj)
// Now, how to convert this ExpandoObject to anonymoustype ?
var newObj = (typeof(anoObj)expandoObj); // This doesn't work
Added Later
// This is my entity
public class Customer
{
#region Public Properties
[ColumnAttribute(Name = "IdColumn")]
public string Id { get; set; }
[ColumnAttribute(Name = "NameColumn")]
public string Name { get; set; }
[ColumnAttribute(Name = "AddressColumn")]
public string Address { get; set; }
[ColumnAttribute(Name = "EmailColumn")]
public string Email { get; set; }
[ColumnAttribute(Name = "MobileColumn")]
public string Mobile { get; set; }
#endregion
}
// -------------------------------------------------------------------------------------
public class LookupService<TEntitySource>
{
public LookupService ()
{
}
public LookupShowable<TEntitySource, TSelection> Select<TSelection>(Expression<Func<TEntitySource, TSelection>> expression)
{
var lookupShowable = new LookupShowable<TEntitySource, TSelection>();
return lookupShowable;
}
}
public class LookupShowable<TEntitySource,TSelection>
{
public LookupShowable()
{
}
public LookupExecutable<TEntitySource, TSelection, TShow> Show<TShow>(Expression<Func<TEntitySource, TShow>> expression)
{
var lookupExecutable = new LookupExecutable<TEntitySource,TSelection,TShow>();
return lookupExecutable;
}
}
public class LookupExecutable<TEntitySource, TSelection, TShow>
{
public TSelection Execute()
{
// Here I want to create a new instance of TSelection and populate values from database and return it.
}
}
//--------------------------------------------------------------------------------------
// This is How I want to call this from front end...
var lookupService = new LookupService<Customer>();
var lookupSelection = lookupService.Select(C => new { C.Id, C.Name, C.Mobile }).Show(C => new { C.Id, C.Name}).Execute();
string sID = lookupSelection.Id;
string sName = lookupSelection.Name;
string sMobile = lookupSelection.Mobile;
Dont think about this middle part.. Purpose of it is another one...
My problem is in Execute() method in LookupExecutable class. I dont know how to create a new instance of TSelection type and assign values to it. This TSelection type is always an anonymous type..
EDIT: I think this question is a prime example of the XY problem. The correct solution doesn't need to concern itself with ExpandoObject or anonymous types, and it would be most likely wrong if it did.
You're looking at it the wrong way. You don't need to create an instance of an anonymous object, you need to invoke the code that is passed to you in an expression (which may or may not be creating an anonymous object).
If you can create an instance of TEntitySource, then that's simple: Compile() the Expression that you got in Select() and then invoke it for each instance of TEntitySource.
If you can't create TEntitySource, you could still do it by rewriting the Expression (using ExpressionVisitor), so that its input is not TEntitySource, but some type you have. But that would require some work from you.
Original answer:
No, that won't work. That's simply not how casting or anonymous types work in C#.
You can't cast between any two types and expect it to work. Either the object you're casting needs to be the type you're casting to, or one of the two types needs to specify a matching cast operator.
The fact that the target type is an anonymous type doesn't change anything (except that you can't even try to cast to an anonymous type directly, because you can't name it; the way you're using typeof() is wrong).
The fact that the source type is dynamic changes things a bit. But only in that the search for the cast operator is done at runtime, not at compile time, and you can even create the cast operator at runtime (see DynamicObject.TryCast()). But that's it, it doesn't add any “magical” cast operators.
The only way I can imagine something like this working would be if you used a variant of “cast by example” and reflection:
public T Convert<T>(ExpandoObject source, T example)
where T : class
{
IDictionary<string, object> dict = source;
var ctor = example.GetType().GetConstructors().Single();
var parameters = ctor.GetParameters();
var parameterValues = parameters.Select(p => dict[p.Name]).ToArray();
return (T)ctor.Invoke(parameterValues);
}
You could then use it something like this:
var expando = new ExpandoObject();
dynamic dynamicExpando = expando;
dynamicExpando.Foo = "SomeString";
dynamicExpando.Bar = 156;
var result = Convert(expando, new { Foo = "", Bar = 1 });
Note that you can't actually invoke Convert() dynamically (by passing it dynamicExpando), because that would mean it would return dynamic too.
Use JavaScriptSerializer to convert the ExpandoObject to any Type as follows:
.....
dynamic myExpandoObject = new ExpandoObject();
var result = ConvertDynamic<myType>(myExpandoObject);
.....
public T ConvertDynamic<T>(IDictionary<string, object> dictionary)
{
var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var obj = jsSerializer.ConvertToType<T>(dictionary);
return obj;
}
This should do the job.
here you have an object made from an ExpandoObject
var anoObj = new { name = "testName", email = "testEmail" };
dynamic expandoObj = new System.Dynamic.ExpandoObject();
object newObj = expandoObj;
But beware, dynamic objects are very very expensive in resource matters, and what you are asking for does not seem to have any sense. A good aproach for what you are asking in the comments supposing you have to deal with dynamic objects and you want to do something with them:
dynamic expando = new System.Dynamic.ExpandoObject();
var myObj = new Dictionary<string, object>();
myObj["myProperty"] = expando.myProperty;
Any dynamyc object is easily casted to a <string, object> typed Dicionary.
Hope that helps!