Having trouble accessing properties on IronPython class from C# - c#

I have some C# code that instantiates an IronPython class and runs some code from it. The IronPython class in question implements an interface that was defined in C#. It also defines some of its own properties.
I am having trouble accessing the properties that are defined by the class itself, and I was wondering if anyone knew how to do this?
Here is an example IronPython class:
import clr
clr.AddReference('System.Core')
from System import Tuple
class TestClass (ITestInterface):
ExamplePropertyOne = System.Tuple[System.String, System.String]("Hello", "World!")
def ExampleMethod(self, some_parameter):
...code...
I create a ScriptEngine object to use the IronPython class, like so:
Dictionary<string, object> options = new Dictionary<string, object>();
options["Debug"] = true;
var python_script_engine = Python.CreateEngine(options);
var python_script_scope = python_script_engine.CreateScope();
python_script_engine.ExecuteFile("my_python_class.py", python_script_scope);
//The class could have any name, so instead of requesting the class by name,
//I simply iterate through the items in the python file to find the items
//that implements the expected interface, like so:
var python_script_items = python_script_scope.GetItems();
python_script_items = python_script_items.Where(x => x.Value is IronPython.Runtime.Types.PythonType);
foreach (var item in python_script_items)
{
System.Type item_type = (Type)item.Value;
var implemented_interfaces = item_type.GetInterfaces();
if (implemented_interfaces != null && implemented_interfaces.Length > 0)
{
if (implemented_interfaces.ToList().Contains(typeof(ITestInterface)))
{
var class_to_instantiate = item.Value;
var class_instance = class_to_instantiate();
...code here to get properties of class...
}
}
}
Just as an FYI, in the foreach loop "item" is a KeyValuePair (a string is the key and a dynamic object is the value). After iterating over many items, the if-statements succeed when we reach the correct key-value pair at which the key is "TestClass" (I've stepped through the code to make sure the if-statement is succeeding at the proper item).
Unfortunately, although we enter the if-statements with "TestClass" as the item's key, when I instantiate the class I don't seem to be able to view the properties that are declared on TestClass. I can only see the methods that are implemented from ITestInterface.
I have tried the following, not knowing which would get me the correct result:
var list1 = Dynamic.GetMemberNames(class_instance); //Using Dinamitey
var list2 = python_script_engine.Operations.GetMemberNames(class_instance);
var fields = item_type.GetFields();
var properties = item_type.GetProperties();
var attributes = item_type.CustomAttributes;
var attributes2 = item_type.GetCustomAttributes(false);
var members = item_type.GetMembers();
var var_names = python_script_scope.GetVariableNames();
None of the above return the property "ExamplePropertyOne" in their results, which is a member of "TestClass". I can only see the implemented methods. And of course the python_script_scope.GetVariableNames only returns info from the scope outside of the instantiated class.
So, any advice? Why am I only able to see the interface members? How can I access the class's properties?
Thanks for any help!

Related

Pass an array or list of different objects to a function for modification

Currently I am receiving an array of objects from a database.
object [] sqlResultData = DatabaseCall.Result();
This array of objects needs to be matched to class variables like this
CClassOfVars classVar = new CClassOfVars();
classVar.myProperty = sqlResultData[0];
classVar.myProperty1 = sqlResultData[1];
What i wish to do is pass the list of propertys on the class in order to a function and have the mapping from the object array occur automatically based on the order.
For example:
Method defined like this
FillData(object [] databaseValues, IList<object>())
Called like this
CClassOfVars classVar = new CClassOfVars();
object [] sqlResultData = DatabaseCall.Result();
FillData(sqlResultData, new List<object>(){classVar.myProperty,classVar.myProperty1});
The FillData function would hopefully type cast and set the values of myProperty and myProperty1 to the values in array locations of 0,1 etc...
Something like this
FillData(object [] databaseValues, IList<object> mapMe)
{
for (int i = 0; i < mapMe.Count; i++)
{
mapMe[i] = CastToTheCorrectType(mapMe[i], databaseValues[i]);
}
}
Cast to the correct type could look like this?? I took from here: cast object with a Type variable
public T CastToTheCorrectType<T>(T hackToInferNeededType, object givenObject) where T : class
{
return givenObject as T;
}
How can i pass a list of different object types to all have there values modified and assigned within a different function?
The matter you asking about is dark and difficult to be implemented through just a function. There are frameworks out there dealing with object relational mapping. If it is an option, install and learn some OR/M. If not ... well, there might be some dirty way.
You can use the JSON.NET library to do the heavy lifting for you. It's super easy to use and install through Nuget. My point is as follows.
Construct an anonymous object. Use the property names of the original object.
Fill it with the data from the object array. Spin a loop over the object array...
Serialize the anonymous object.
Deserialize the JSON string into the target type.
At this point, JSON.NET will handle property mapping for you.
List item
E.g. if your target type is Person you might do this:
var x = new
{
FirstName = String.Empty,
LastName = String.Empty
};
var persons = new List<Person>(sqlResultData.Length);
foreach (var record in sqlResultData)
{
x.FirstName = record[0];
x.LastName = record[1];
var s = JsonConvert.SerializeObject(x)`
var personX = JsonConvert.Deserialize<Person>(s);
persons.Add(person);
}

Get and save enum using reflection

I have a project in which I have some assemblies which implement an abstract class.
Each assembly has a public enum called ResultEnum.
This ResultEnum's value is stored in a database as an int.
I have another web project which displays some info, and I want it to also display this int's string representation - the name of the corresponding value from the ResultEnum.
What I want to do is, using MEF, load all the relevant assemblies (no problem here), search for this enum using reflection (no problem here also) and then to store the enum in some way, and cache it in order to avoid all this process the next time I want to convert the int from the database to the string representation (and the other way around if necessary) since I have several thousands of records in my db table.
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(path));
_container = new CompositionContainer(catalog);
try
{
_container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
foreach (var task in myTasks)
{
TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
MemberInfo[] infos = instance.GetType().GetMembers(BindingFlags.Public | BindingFlags.Static);
foreach (MemberInfo member in infos.Where(x => x.Name.Equals("ResultEnum")))
{
Console.WriteLine(member);
}
}
What do you suggest the next move should be?
How should I store/cache it?
Thanks
In addition to #Thomas's answer:
As using reflection you can get exact int value from a property, the name for that concrete value could be gotten using the next expression:
var enumValueName = Enum.GetName(member.GetType(), member.GetValue(instance));
UPD
I really missed that you reflect MemberInfos. To apply my solution you can update you reflection this way:
foreach (var task in myTasks)
{
TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
// Reflect properties, not all members
PropertyInfo[] infos = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo prop in infos.Where(x => x.Name.Equals("ResultEnum")))
{
var enumValueName = Enum.GetName(prop.GetType(), prop.GetValue(instance));
}
}
Or you could cast MemberInfo to PropertyInfo.
One approach to solving this problem is to consider using a subclassable enums technique (also sometimes referred to as a polymorphic enum).
I wrote a couple of generic classes specifically to support these kinds of types which you can read about here. Also, a proposal has been submitted to the Roslyn compiler team on Github to add support for these types of enums to C#.
Here is an example of a set of subclassable enums that have two underlying types, string and integer, using the classes from my project:
public sealed class Status : StringIntegerEnum<Status>
{
public static readonly Status Active = new Status("active", 1);
public static readonly Status Inactive = new Status("inactive", 0);
private Status(string status, int statusCode) : base(status, statusCode) {}
}
Note that the string value is not the same as the constant name itself, which allows you to have underlying string values with characters that violate the normal naming conventions in C#.
The StringIntegerEnum<tStringIntegerEnum> base class provides .AllValues, .AllNaturalValues and .AllStringValues static methods that you can use to enumerate the list of enum values or both types of their underlying values.
From your comment:
I agree, But question is how do I iterate over the values and names of the enumerator
I assume you mean "enumeration", not "enumerator". You can use the Enum.GetValues method:
var valuesToNames =
Enum.GetValues(enumType)
.Cast<object>()
.ToDictionary(o => (int)o, o => Enum.GetName(enumType, o));
And, is there a better solution than a dictionary
Better how? I think a dictionary is a fine solution; is there any reason why you would want something else?
This is how i've written the code eventually:
resultEnumsForTasks = new Dictionary<string, Dictionary<UInt16, string>>();
foreach (var task in myTasks)
{
Dictionary<UInt16, string> _enum = new Dictionary<UInt16,string>();
TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
MemberInfo resultEnum = instance.GetType().GetMember("ResultEnum").FirstOrDefault();
if (resultEnum == null)
continue;
string[] names = Enum.GetNames(resultEnum as Type);
IList<int> vals = (IList<int>)Enum.GetValues(resultEnum as Type);
for (int i = 0; i < names.Length; i++)
{
_enum.Add(Convert.ToUInt16(vals[i]), names[i]);
}
resultEnumsForTasks.Add(instance.GetType().Name, _enum);
}
It's very similar to #n.turakulov 's solution, however his solution didn't work for me since I got an empty list of PropertyInfo for some reason...
Thanks for everyone who assisted!

Not able to get dynamic properties with reflection

A dynamic object is generated using a Json deserializing component (Jil) I am using, and I am able to access the properties directly. But I don't know their names in advance, so I am trying to get the names with reflection. I tried doing this:
var props = myDynObj.GetType().GetProperties();
but the page times out. Doesn't give me anything in debugger, just sits there doing nothing, or something and not telling me.
This even happens when I even do this:
var t = myDynObj.GetType();
But when I do this, it works:
var val = myDynObj.MyStaticValue1
Just can't really do anything else with it. Anyonw know why, and how I can get this to work?
Please allow me to note:
Before I get started, if you don't know the members already when you're parsing JSON, you should not be parsing into a dynamic object. The built-in .Net JavaScriptConverter class can parse JSON into a IDictionary<string, object> which would be much better for you.
However, if you still want to use dynamic objects for some reason:
If you want to stick with your current library: I dont know how exactly that class is working, and I'm not saying this is the best solution, but by looking at the source it jumps out to me that you can grab a list of the ObjectMembers keys using reflection.
Type t = typeof(JsonObject)
var fi = t.GetField("ObjectMembers", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
IEnumerable<string> keys = ((Dictionary<string, JsonObject>)fi.GetValue(obj)).Keys;
Edit: Seeing that JsonObject implements IDynamicMetaObjectProvider, the following method mentioned in this question will also work on it:
public static IEnumerable<string> GetMemberNames(object target, bool dynamicOnly = false)
{
var tList = new List<string>();
if (!dynamicOnly)
{
tList.AddRange(target.GetType().GetProperties().Select(it => it.Name));
}
var tTarget = target as IDynamicMetaObjectProvider;
if (tTarget !=null)
{
tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTarget)).GetDynamicMemberNames());
}else
{
if (ComObjectType != null && ComObjectType.IsInstanceOfType(target) && ComBinder.IsAvailable)
{
tList.AddRange(ComBinder.GetDynamicDataMemberNames(target));
}
}
return tList;
}
If you are open to trying a different JSON converter: try this class here: http://pastie.org/private/vhwfvz0pg06zmjqirtlxa I'm not sure where I found it (I can't take credit) but here is an example of how to use it how you want:
// Create dynamic object from JSON string
dynamic obj = DynamicJsonConverter.CreateSerializer().Deserialize("JSON STRING", typeof(object));
// Get json value
string str = obj.someValue;
// Get list of members
IEnumerable<string> members = (IDictionary<string, object>)obj).Keys
Personally I like using the second one, it is simple and easy to use - and builds off of the built in .Net JSON parser.

Attempting to obtain properties / fields from an (anonymous class) object linq is creating

I'm having trouble figuring out what I'm doing wrong here. I have some LINQ that returns an IQuery object, and later in the code, I'm attempting to list out the attributes returned. This is best explained by this abbreviated code (the actual LINQ is a lot more complex and involves joins - the LINQ itself works fine):
public IQueryable<Object> FindAll()
{
var query = from user in _db.Users
select new
{
id = user.Id,
is_active = user.IsActive,
email = user.Email,
dob = user.Dob,
user.user_type,
};
return query;
}
Elsewhere in the code I have:
query.ConvertToCsv();
Although I have attempted to insert a .ToList() in that call as well.
The ConvertToCsv has:
public static string ConvertToCSV<TSource>(this IEnumerable<TSource> source)
{
StringBuilder sb = new StringBuilder();
var properties = typeof(TSource).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
var enumerable = source as IList<TSource> ?? source.ToList();
if (!enumerable.Any()) return "";
string headerString = "";
foreach (var prop in properties)
{
headerString += (headerString.Length > 0 ? "," : "") + prop.Name;
}
sb.AppendLine(headerString);
foreach (TSource item in enumerable)
{
string line = string.Join(",", properties.Select(p => p.GetValue(item).ToCsvValue()).ToArray());
sb.AppendLine(line);
}
return sb.ToString();
}
Note I have also tried to pull out the property names with this code:
PropertyInfo[] pi = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var properties = pi.OrderBy(x => x.MetadataToken);
foreach (PropertyInfo p in properties)
{ etc etc }
In all cases, the property or field list returns an empty list, and as such, I can't iterate through the object to spit out a header row or data rows. Tracing through all the code and inspecting the variables indicates that everything is fine until I get to the GetProperties/GetFields line and the code fails.
What rookie mistake am I making? Should I be replacing <Object> with something else?
To pass an anonymous type, or a collection that contains anonymous
types, as an argument to a method, you can declare the parameter as
type object. However, doing this defeats the purpose of strong typing.
If you must store query results or pass them outside the method
boundary, consider using an ordinary named struct or class instead of
an anonymous type.
by Anonymous Types (C# Programming Guide)
Create your own class and change method declaration to be IQueryable<MyClass> instead of object
Did you consider doing something like: db.Users.Select(u => new UserDto() { Id = user.Id, Name = ..., where UserDto is dedicated class that has all the properties you'll need in the future? I think you lose information about properties when you cast from anonymous class to an Object. Although, I never tried to obtain member info from anonymous class

LINQ - Add property to results

Is there a way to add a property to the objects of a Linq query result other than the following?
var query = from x in db.Courses
select new
{
x.OldProperty1,
x.OldProperty2,
x.OldProperty3,
NewProperty = true
};
I want to do this without listing out all of the current properties of my object. There are many properties, and I don't want to have to update this code whenever I may change my class.
I am still learning with LINQ and I appreciate your suggestions.
Add it with partial classes:
public partial class Courses
{
public String NewProperty { get; set; }
}
Then you can assign it after you've created the object.
I suppose you could return a new object composed of the new property and the selected object, like this:
var query = from x in db.Courses
select new
{
Course = x,
NewProperty = true
};
eking's answer will be the most straightforward approach.
If that doesn't work for you (because you need to pass the results around or whatever), and assuming the class you're dealing with already defines the property you want to set, you could create a copy constructor or factory method that takes an existing instance plus the value of the property you want to set:
var query = from x in db.Courses
select new Course(x, valueOfNewProperty);
Alternatively, if Course doesn't define the property, you could subclass it and use the same approach:
var query = from x in db.Courses
select new CourseWithExtraProperty(x, valueOfNewProperty);
(obviously, pick a better name for your subclass)
Again, though, unless you really need to do this, stick with eking's solution.
ServiceStack has a built-in way to handle this with the PopulateWith method.
Here's a code example.
foreach (var item in results)
{
var test1 = new ItemDto().PopulateWith(item);
test1.extraField1 = "extra";
response.Add(test1);
}`
And if you're not using ServiceStack, you can always use AutoMapper.
CreateMap<Foo, Bar>().ForMember(x => x.ExtraBarProperty, opt => opt.Ignore());
If you are looking to dynamically add a property to an object this could be a solution.
This is what has worked for me, I also had a concern and it was what happened with those domain objects that had many properties, the maintainability for any changes in the object was absurd, I managed to build an implementation with LINQ - ExpandObject - Reflection, which helped to keep my object dynamic and only add the additional properties that my view logic required.
var expandedModel = db.Courses.Select(x =>
{
dynamic expandObject = new ExpandoObject();
expandObject.NewProperty= $"PropertyValue";
foreach (var property in x.GetType().GetProperties())
{
((IDictionary<string, object>)expandObject).Add(property.Name, property.GetValue(x));
}
return expandObject;
}).ToList();

Categories

Resources