I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.
Related
Consider the following class -
public class User
{
[Selected]
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Selected]
public int Code { get; set; }
public object GetValue()
{
// need to do something here
}
}
[Selected] here is nothing but a marker attribute. I want GetValue method to return an object which will have the [Selected]-marked properties with corresponding values. That is, in the code below -
private static void Execute()
{
User user = new User
{
Name = "alice",
Email = "alice#liddell.com",
Password = "123456",
Code = 1234
};
var value = user.GetValue();
}
value should be an object with two properties Name and Code which should have the values "alice" and 1234 respectively.
After some searching I tried ExpandoObject (which I never used before) -
public object GetValue()
{
var dictionary = this.GetType().GetProperties().Where(p => p.GetCustomAttribute(typeof(Selected), false) != null).ToDictionary(p => p.Name);
dynamic expando = new ExpandoObject();
foreach (var item in dictionary)
{
object value = item.Value.GetValue(this);
((IDictionary<string, object>)expando).Add(item.Key, value);
}
return expando;
}
But it didn't serve my purpose - the client/consumer of value object somehow couldn't read/access the property values.
Any suggestions?
Edit :
There might be a lot of classes like User and the GetValue method will be called from within a generic method. So, at runtime I have no way to know what type the object is and which properties are marked.
To access the fields by name it is easier to cast the returned object to IDictionary:
var value = (IDictionary<string, object>) user.GetValue();
Console.WriteLine(value["Name"]);
Simplify your method to this:
public Dictionary<string, object> GetValue()
{
var dictionary = this.GetType()
.GetProperties()
.Where(p => p.GetCustomAttribute(typeof(Selected), false) != null)
.ToDictionary(p => p.Name, p => p.GetValue(this));
return dictionary;
}
Use:
var value = user.GetValue(); // value is Dictionary
foreach (var kvp in value)
{
Console.WriteLine(kvp);
}
If you wish POCO, then you can do it like follows
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public int Code { get; set; }
public SelectedUserProperties GetValue()
{
return new SelectedUserProperties
{
Name = Name,
Code = Code
};
}
}
public class SelectedUserProperties
{
public string Name { get; set; }
public int Code { get; set; }
}
It is assumed that the selected properties are known in advance, before compilation.
This makes the marker attribute unnecessary and can be completely removed.
What you did works well, you just have to use the keyword dynamic when you call the method GetValue.
dynamic value = user.GetValue();
If you are using var, value will be of the same type as the return type of your function (i.e object) at compile time. Therefore, if you try to do value.Name your compiler won't allow it because the class Object doesn't have any attribute Name.
dynamic tells your program to do the type checking at runtime.
I have a class
[BsonIgnoreExtraElements]
public class CustomerModel
{
public string BranchID { get; set; }
public string BranchName { get; set; }
public string ReceiverID { get; set; }
public string ReceiverName{ get; set; }
}
I am writing a filter activity which can validate any field with specific value configured in MongoDB
"Exclude":[{"SourceCol":"Receiver Mode ID","Values":{"Value":["G","8","J"]}}
and written the comparing logic as
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
public static bool CheckPropertyCompare(CustomerModel customer, Exclude item)
{
var propertyValue = GetPropValue(customer, item.SourceCol);
return item.Values.Value.ToList().Contains(propertyValue);
}
In this, the Receiver Mode ID from MongoDB is actually looking for ReceiverID and I am stuck as to how can I resolve this issue. The only option I can think of is Key-Value pair collection to fetch the field name. but would like to know if there is any options like Attributes which can ease this process.
TIA
I think you can achieve that with Attributes as you say.
You can create a custom attribute, like this:
internal class MongoDBFieldAttribute : Attribute
{
public string Field{ get; private set; }
public MongoDBFieldAttribute(string field)
{
this.Field= field;
}
}
Then in your class:
public class CustomerModel
{
...
[MongoDBField("ReceiverModeID")]
public string ReceiverID { get; set; }
}
I think it could be better without spaces, it could be a problem, maybe yo can use a Trim() or similar... or yoy can try [MongoDBField("Receiver Mode ID")], never tried.
Then you can create a method than can relation both, property name and attribute name, for example:
private Dictionary<string, string> getRelationPropertyAttribute(Type type)
{
var dicRelation = new Dictionary<string, string>();
var properties = type.GetProperties();
foreach (var property in properties)
{
var attributes = property.GetCustomAttributes(inherit: false);
var customAttributes = attributes
.AsEnumerable()
.Where(a => a.GetType() == typeof(MongoDBFieldAttribute));
if (customAttributes.Count() <= 0)
continue;
foreach (var attribute in customAttributes)
{
if (attribute is MongoDBFieldAttribute attr)
dicRelation[attr.Field] = property.Name;
}
}
return dicRelation;
}
Finally, you can play with that dictionary and in your method you can do something like that:
public static bool CheckPropertyCompare(CustomerModel customer, Exclude item)
{
var dicRelation = getRelationPropertyAttribute(typeof(CustomerModel));
var propertyName = dicRelation[item.SourceCol];
var propertyValue = GetPropValue(customer, propertyName);
return item.Values.Value.ToList().Contains(propertyValue);
}
It´s an idea...
Hope it helps.
I have come upon an issue I can't seem to figure out. I'm sure there's a simple explanation to this, but I don't understand why I get a System.Reflection.TargetException: 'Object does not match target type' when I try to get a property from (in this case) the employee object.
employee.GetType().GetProperty(property.Name)
Searching for the error returns many results describing problems with calling the Set/GetValue methods, but I haven't found a solution to this one.
I've set a breakpoint where the exception is thrown and it shows that property.Name is indeed a value - and a real property of the object. I've also tried manually specifying a property I know exists. Still the same.
Any suggestions?
EDIT: Tried the following instead:
Type type = typeof (Employee); //Throws the TargetException
PropertyInfo theProperty = type.GetProperty(property.Name);
And now the same exception is thrown at the first line above instead.
EDIT: Added code and more details about the application I'm building.
Class definition for Employee (to simplify mapping to the JSON data this class "represents", the class/fields are in Norwegian - which is the format/language the data comes in, sorry :-).)
"Ansatt" = Employee. "Ansattnummer" = EmployeeNo.
[JsonObject]
public class Ansatt
{
public int Ansattnummer { get; set; }
public string Fornavn { get; set; }
public string Etternavn { get; set; }
public int Pin { get; set; }
public string Adresse { get; set; }
public int Postnummer { get; set; }
public string Poststed { get; set; }
public int TlfPrivat { get; set; }
public int MobilTlf { get; set; }
public string EpostAdresse { get; set; }
public DateTime Fodt { get; set; }
}
My application retrieves a given dataset from a web service - it could be employees, projects or a few other possible datasets. What data to fetch is determined at runtime - by the user. The user can also specify via URL-query which portions, e.g. columns, of the dataset he/she wants. The program then creates a csv-file with the selected data.
Here's the code I use for this:
if (records != null && records.Count != 0) //records contains the chosen dataset - in this case Employees (Ansatt).
{
if (records.GetType() == typeof (List<Ansatt>))
{
foreach (var model in records as List<Ansatt>)
{
var temp = new Ansatt();
foreach (var property in model.GetType().GetProperties())
{
var currentProperty = model.GetType().GetProperty(property.Name);
if (currentProperty != null)
{
Type type = typeof (Ansatt); //Throws System.Reflection.TargetException: 'Object does not match target type'
PropertyInfo tempProperty = type.GetProperty(property.Name);
tempProperty.SetValue(temp, currentProperty.GetValue(property.Name));
}
}
csv.WriteRecord(temp);
}
}
}
You need to specify the name of the property
PropertyInfo value = employee.GetType().GetProperty("Name");
As MSDN goes, you should use it this way:
class MyClass {
private int myProperty;
// Declare MyProperty.
public int MyProperty {
get {
return myProperty;
}
set {
myProperty = value;
}
}
}
public class MyTypeClass {
public static void Main(string[] args) {
try {
// Get the Type object corresponding to MyClass.
Type myType = typeof(MyClass);
// Get the PropertyInfo object by passing the property name.
PropertyInfo myPropInfo = myType.GetProperty("MyProperty");
// Display the property name.
Console.WriteLine("The {0} property exists in MyClass.", myPropInfo.Name);
// Instantiate MyClass
var myObject = new MyClass()
{
MyProperty = 5
};
// Get value using reflection
Console.WriteLine("My property value for my object is {0}.", myPropInfo.GetValue(myObject));
} catch (NullReferenceException e) {
Console.WriteLine("The property does not exist in MyClass." + e.Message);
}
}
}
For your code, when you want to get the property value of an object instance, you should pass object as reference to PropertyInfo.GetValue(object) function.
Instead of this:
tempProperty.SetValue(temp, currentProperty.GetValue(property.Name));
Do this:
tempProperty.SetValue(temp, currentProperty.GetValue(model));
For you to get the property of object through reflection, make it sure that the property name is public with getter and setter else it will return null.
Ex.
public class Employee
{
public string YouProperty { get; set; }
}
var employee = new Employee();
var result = employee.GetType().GetProperty("YouProperty");
// The result is property info
Kindly read some information here.
Is there an easy way to copy everything from a strongly typed object into a dynamic one? The target has to be a DynamicObject as determined by a 3rd party library I'm using. Everything from TypedModel needs to go into MyDynamicObject at runtime.
public class MyDynamicObject : DynamicThirdPartyObject
{ }
public class TypedModel
{
public string text { get; set; }
public int number { get; set; }
public List<SomeOtherModel> someList { get; set; }
}
Existing solutions I found on SO all match up properties between typed classes.
EDIT
Found a simple solution based on FastMember:
public void CopyProperties(object source, DynamicObject target)
{
var wrapped = ObjectAccessor.Create(target);
foreach (var prop in source.GetType().GetProperties())
{
wrapped[prop.Name] = prop.GetValue(source);
}
}
I propoes to use reflection.
suppose you make following declaration:
public class MyDynamicObject : DynamicThirdPartyObject
{ }
public class TypedModel
{
public string text { get; set; }
public int number { get; set; }
public List<SomeOtherModel> ListOtherModel { get; set; }
}
Lets say you want to get properties of instance:
typedModel.GetType().GetProperties();
Another possible situation is if you want to copy type:
typeof(TypedModel).GetProperties();
TypedModel typeModel = new TypedModel {number = 1, text = "text1",
ListOhterModel = new List()
};
foreach(var prop in typeModel.GetType().GetProperties())
{
Console.WriteLine("{0}={1}", prop.Name, prop.GetValue(typeModel, null));
}
And if you need to go through hierarchy, maybe you need to use recursion, in order to go through nested types, I mean you can use reflection for copying all members of SomeOtherModel.
I have a model
public class User : EntityObject {
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
and I would like to return to the client a JSON result containing only the Username and Password members of the User.
I am currently doing this like so
return Json(new { MyUser.Username, MyUser.Password });
But I would like to be able to have an interface
public interface ClientAvailableUser {
string Username { get; set; }
string Password { get; set; }
}
and use it to know what to return to the client
How can I use interface ClientAvailableUser to create a new object from User that has only the members from the User also present in the interface ?
User MyUser = new User();
// MyUser has an Id, Username and Password
Object FilteredMyUser = // Filter MyUser using the ClientAvailableUser interface so that
// FilteredMyUser has only Username and Password according to ClientAvailableUser interface
Another option (and my personal pereference) would be to simply put System.Web.Script.Serialization.ScriptIgnoreAttribute on the members of the model class that you don't want serialized (or create an implicitly-convertible DTO class to do the same thing).
Ex:
using System.Web.Script.Serialization;
public class User
{
[ScriptIgnore]
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
That way you don't need to define a special interface, you can put this metadata right in your model.
Update: Apparently that's not an option because the class is a derived class and it's members from the (unmodifiable) base class that should be hidden.
It is possible to dynamically generate a class the way you want, either using Emit or a dynamic proxy library like Castle, but it's going to be very cumbersome. If you can, I would really recommend to use a simple proxy class instead:
public class UserResult
{
public UserResult(User user)
{
Username = user.Username;
Password = user.Password;
}
public string Username { get; set; }
public string Password { get; set; }
}
Or, if you really can't deal with maintaining this, you can build a "generic" proxy instantiator:
static class ProxyInstantiator
{
public static TProxy CreateProxy<TProxy>(object source)
where TProxy : new()
{
TProxy proxy = new TProxy();
CopyProperties(source, proxy);
return proxy;
}
protected static void CopyProperties(object source, object dest)
{
if (dest == null)
{
throw new ArgumentNullException("dest");
}
if (source == null)
{
return;
}
Type sourceType = source.GetType();
PropertyInfo[] sourceProperties =
sourceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
Type destType = dest.GetType();
PropertyInfo[] destProperties =
destType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var propsToCopy =
from sp in sourceProperties
join dp in destProperties on sp.Name equals dp.Name
select new { SourceProperty = sp, DestProperty = dp };
foreach (var p in propsToCopy)
{
object sourceValue = p.SourceProperty.GetValue(o, null);
p.DestProperty.SetValue(dest, sourceValue, null);
}
}
}
Then you can write a simple proxy class (not interface):
public class UserResult
{
public string Username { get; set; }
public string Password { get; set; }
}
And invoke it in a controller method like this:
User someUser = GetSomeUser();
UserResult result = ProxyInstantiator.CreateProxy<UserResult>(someUser);
A word of caution about this:
This does not take into account indexed properties and will fail if there are any of those. It does not take into account "deep copying" - if your source class contains reference types, it will only copy the references - maybe that's what you want, maybe it isn't.
Personally, I'd take the former approach and just build individual proxy classes without the generic proxy, because if I make a mistake, I'd prefer a compile-time error over a runtime error. But you asked, so there you go!
Something like this will give you a list of properties in object with the same name as a propety in the interface (haven't compiled this yet, but it should be close). This should get you started.
public IEnumerable<PropertyInfo> GetPropertiesToTransfer( User user );
{
Type userType = user.GetType();
Type clientAvailableType = typeof(ClientAvailableUser);
PropertyInfo[] userProps = userType.GetProperties();
IEnumerable<string> caUserProps = clientAvailableType.GetProperties().Select( p => p.Name );
return userProps.Where( p => caUserProps.Contains( p.Name ) );
}
I'm really not sure I get the "why" you're trying to do what you're doing, but you can do a number of things:
public interface IClientAvailableUser
{
string Username { get; set; }
string Password { get; set; }
}
internal class ConcreteClientAvailableUser : IClientAvailableUser
{
public string UserName{get;set;}
public string Password{get;set;}
}
public class UserExtensions
{
public IClientAvailableUser AsClientAvailableUser(this User user)
{
return new ConcreteClientAvailableUser { UserName = user.UserName, Password = user.Password};
}
}
Then you can just do this:
IClientAvailableUser ica = myUser.AsClientAvailableUser();
But I don't understand why your user class can't just implement your interface directly, and then you could do:
IClientAvailableUser ica = myUser;
Yes, this isn't a NEW object, but what do you need a new one for anyway?