C# working with decorated members - c#

Take this class for example:
public class Applicant : UniClass<Applicant>
{
[Key]
public int Id { get; set; }
[Field("X.838.APP.SSN")]
public string SSN { get; set; }
[Field("APP.SORT.LAST.NAME")]
public string FirstName { get; set; }
[Field("APP.SORT.FIRST.NAME")]
public string LastName { get; set; }
[Field("X.838.APP.MOST.RECENT.APPL")]
public int MostRecentApplicationId { get; set; }
}
How would I go about getting all of the properties that are decorated with the field attribute, get their types, and then assign a value to them?

This is all done with reflection. Once you have a Type object, you can then get its PropertyInfo with myType.GetProperties(), from there, you can get each property's attributes with GetCustomAttributes(), and from there if you find your attribute, you've got a winner, and then you can proceed to work with it as you please.
You already have the PropertyInfo object, so you can assign to it with PropertyInfo.SetValue(object target, object value, object[] index)

You'll need to use Reflection:
var props =
from prop in typeof(Applicant).GetProperties()
select new {
Property = prop,
Attrs = prop.GetCustomAttributes(typeof(FieldAttribute), false).Cast<FieldAttribute>()
} into propAndAttr
where propAndAttr.Attrs.Any()
select propAndAttr;
You can then iterate through this query to set the values:
foreach (var prop in props) {
var propType = prop.Property.PropertyType;
var valueToSet = GetAValueToSet(); // here's where you do whatever you need to do to determine the value that gets set
prop.Property.SetValue(applicantInstance, valueToSet, null);
}

You would just need to invoke the appropriate reflection methods - try this:
<MyApplicationInstance>.GetType().GetProperties().Where(x => x.GetCustomAttributes().Where(y => (y as FieldAttribute) != null).Count() > 0);

Related

mapping between an object and a dynamically created object after JSON deserialization

How do I do a mapping between an object created by instantiating a class and a dynamic object created by a JSON deserialization (JsonConvert) considering I don't know the dynamic object's fields? In other words, I'd like to update the dynamic object fields matching by name.
This is my example code:
string json = {\"NDG\":7803, \"NumberOfNights\":2, \"Nome\":\"Ago\", \"Cognome\":\"Mar\", \"CognomeNome\":\"\"};
string djson = ?? //I don't know the structure coming from a call as parameter but I know there are some json string identical fields
public class myVars
{
public string Userid { get; set; }
public string Nome { get; set; }
public string Cognome { get; set; }
public string CognomeNome { get; set; }
}
myVars object1 = JsonConvert.DeserializeObject<myVars>(json);
dynamic object2 = JObject.Parse(djson); // object2 contains a field named "CognomeNome"
myVars.CognomeNome = myVars.Cognome + myVarsNome;
MapObjects(object1 , object2);
string rjson = JsonConvert.SerializeObject(object2); //returns {"CognomeNome":""}
public static object MapObjects(object source, object target)
{
foreach (PropertyInfo sourceProp in source.GetType().GetProperties())
{
PropertyInfo targetProp = target.GetType().GetProperties().Where(p => p.Name == sourceProp.Name).FirstOrDefault();
if (targetProp != null && targetProp.GetType().Name == sourceProp.GetType().Name)
{
targetProp.SetValue(target, sourceProp.GetValue(source));
}
}
return target;
}
The simplest way is to use one of the existing automappers (Alternatives to AutoMapper)
For example, this:
https://github.com/agileobjects/AgileMapper/wiki/Performing-Updates sounds like it's what you are asking for.

Linq query - "Where" on List<T> Where reflected property Contains text/value

I would like to build a Function where user could search if certain property from list contains value
Let say we will have List, and Company will be defined as a class with properties like :
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyPostCode { get; set; }
public string CompanyCity { get; set; }
public string CompanyCounty { get; set; }
}
Now - Ideally I would like to have function with this parameters
List<Company> FilterCompanies(List<Company> unfilteredList, string fieldToQueryOn, string query)
{
// linq version what ideally would like to archeve
return unfilteredList.Where(x => x."fieldToQueryOn".ToString().ToLower().Contains(query.ToLower())).ToList();
}
and call :
var variable = FilterCompanies(NotNullFilledUnfilteredList, "CompanyCity", "New York")
I tried to follow the tutorial at learn.microsoft.com and it's easy, but I don't have clue how to extend that solution with reflection on Type and use it in an expression tree.
You can use Type.GetProperty to find a property by name using reflection, and then use GetValue to retrieve the value:
List<Company> FilterCompanies(List<Company> list, string propertyName, string query)
{
var pi = typeof(Company).GetProperty(propertyName);
query = query.ToLower();
return list
.Where(x => pi.GetValue(x).ToString().ToLower().Contains(query))
.ToList();
}
You should probably add some error handling though in case someone uses a property that is invalid. For example, you could do (pi?.GetValue(x) ?? string.Empty).ToString().ToLower()… to be on the safe side.
I’ve also moved the query.ToLower() out of the lambda expression to make sure it only runs once. You can also try other case-insensitive ways to check whether query is a substring of the value to avoid having to convert any string. Check out the question “Case insensitive 'Contains(string)'” for more information.
Btw. if you are generally interested in running dynamic queries, you should take a look at dynamic LINQ.
Generics and lambda:
namespace WhereTest
{
class Program
{
static void Main(string[] args)
{
var companies = new[] { new Company { Id = 1, Name = "abc" }, new Company { Id = 2, CompanyAddress1 = "abc" } };
foreach (var company in FilterCompanies(companies, "abc", x => x.Name, x => x.CompanyCity))
{
Console.WriteLine(company.Id);
}
}
static List<Company> FilterCompanies(IEnumerable<Company> unfilteredList, string query, params Func<Company, string>[] properties)
{
return unfilteredList.Where(x => properties.Any(c => c.Invoke(x) == query)).ToList();
}
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyPostCode { get; set; }
public string CompanyCity { get; set; }
public string CompanyCounty { get; set; }
}
}
Advantages: no reflection, strongly typed code.
You can use GetProperty combined with GetValue
List<Company> FilterCompanies(List<Company> unfilteredList, string fieldToQueryOn, string query)
{
return unfilteredList
.Where(x => x.GetType.GetProperty(fieldToQueryOn).GetValue(x)
.ToString().ToLower().Contains(query.ToLower())).ToList();
}
OR: property accessors using string (same as javascript obj[property])
You can modify your class:
public class Company
{
// just add this code block to all your classes that would need to access
// your function
public object this[string propertyName]
{
get{
Type myType = typeof(Company);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set{
Type myType = typeof(Company);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
public int Id { get; set; }
public string Name { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyPostCode { get; set; }
public string CompanyCity { get; set; }
public string CompanyCounty { get; set; }
}
and then you can change your function like this:
List<Company> FilterCompanies(List<Company> unfilteredList, string key, string query)
{
// linq version what ideally would like to archeve
return unfilteredList.Where(x => x[key].ToString().ToLower().Contains(query.ToLower())).ToList();
}
Check this Demo
NOTE:
In order for your function to work, you need to add this code to your classes:
public object this[string propertyName]
{
get{
Type myType = typeof(<YOUR CLASS HERE>);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set{
Type myType = typeof(<YOUR CLASS HERE>);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
Bonus: you can now retrieve values using myObject["someproperty"] and you can even set their values!

.net get Properties using Reflection at Multilevel

Sorry if this is a duplicate. I searched and was not able to find an answer.
How do I use reflection to get Values of Properties of class at multilevel?
I have a List of string that has some string values like this:
ClassNames = {"FirstName", "LastName", "Location.City", "Location.State", "Location.Country", "PhoneNo"}
I have two classes
public class Details
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Location Location { get; set; }
public string PhoneNo { get; set; }
}
public class Location
{
public long City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
I used reflection and I am able to get the values of firstname, lastname and phone. But how do I get the values in the location class? It throws an Error. I change the List to just have Location / City . I am missing something. I dont want to do Multiple for loops as the level might drill down to n level. (4 max to be realistic)
foreach (string fieldname in ClassNames)
{
string fieldvalue = RestultDTO[indexValue]GetType().GetProperty(fieldname) ;
}
You'd have to get the Location instance first:
var s = "Location.City";
var sVals = s.Split('.');
// here [t] is the TypeInfo of [Details]
var l = t.GetProperty(sVals[0]);
^ gets the Location PropertyInfo (really only need to do this once
var val = l.GetProperty(sVals[1]).GetValue(l.GetValue(o));
^ gets the PropertyInfo for the property you're after (City in this case)
^ gets the actual value of that property
^ gets the value of Location for o where o is an instance of Details
If you're using a version before 4.5 you'll probably need to send in one additional parameter to the GetValue method - it can be , null both times because the properties aren't indexers.
Here are two methods I wrote to solve this issue
This one gets a queue of properties from a base object:
private static Queue<PropertyInfo> GetProperty(object obj, string path)
{
Queue<PropertyInfo> values = new Queue<PropertyInfo>();
Type object_type = obj.GetType();
string[] properties = path.Split('.');
PropertyInfo propertyInfo = null;
foreach (string property in properties)
{
if (propertyInfo != null)
{
Type propertyType = propertyInfo.PropertyType;
propertyInfo = propertyType.GetProperty(property);
values.Enqueue(propertyInfo);
}
else
{
propertyInfo = object_type.GetProperty(property);
values.Enqueue(propertyInfo);
}
}
return values;
}
And this one uses the queue and the object to get the actual values:
private static T GetValue<T>(object obj, Queue<PropertyInfo> values)
{
object location = obj;
while (values.Count > 0)
{
location = values.Dequeue().GetValue(location);
}
return (T)location;
}
Probably wouldn't hurt to add some error checking and such as well.

C# Property SetValue cannot handle generic List type

In my WinForm application, I'm actually using the LightweightDAL project which provide a nice method to fill up my datamodel. All works fine except for a properties set as List of another model. This is my model class:
public class SchedaFamiglia
{
[DBField("Id")]
public int Id { get; set; }
[DBField("IdFamiglia")]
public int IdFamiglia { get; set; }
[DBField("IdMamma")]
public int IdMamma { get; set; }
[DBField("IdPapa")]
public int IdPapa { get; set; }
[DBField("IdBambino")]
public List<SchedaBambino> Figli { get; set; }
public SchedaFamiglia()
{
}}
The model SchedaBambino above, is just a list of base type properties (int32, string...).
And this is the snippet of GetItemFromReader method:
private static T GetItemFromReader<T>(IDataReader rdr) where T : class
{
Type type = typeof(T);
T item = Activator.CreateInstance<T>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
// for each property declared in the type provided check if the property is
// decorated with the DBField attribute
if (Attribute.IsDefined(property, typeof(DBFieldAttribute)))
{
DBFieldAttribute attrib = (DBFieldAttribute)Attribute.GetCustomAttribute(property, typeof(DBFieldAttribute));
if (Convert.IsDBNull(rdr[attrib.FieldName])) // data in database is null, so do not set the value of the property
continue;
if (property.PropertyType == rdr[attrib.FieldName].GetType()) // if the property and database field are the same
property.SetValue(item, rdr[attrib.FieldName], null); // set the value of property
else
{
// change the type of the data in table to that of property and set the value of the property
property.SetValue(item, Convert.ChangeType(rdr[attrib.FieldName], property.PropertyType), null);
}
}
}
The method above unfortunately can't handle List of generic and I've no idea how to implement.
Someone could help me?

How can reflection be used to determine if an attribute exists on a property?

Similar: How I can find Data Annotation attributes and their parameters using reflection
However, when attempting to gather the custom attribute, I always get back the same result. An empty ScriptIgnore.
PropertyInfo[] Properties = Entity.GetType().GetProperties();
foreach (PropertyInfo Property in Properties)
Upon debug, this line of code
var annotes = Property.GetCustomAttributes(typeof(ScriptIgnoreAttribute), false);
(I also tried using true)
looks like this
annotes | {System.Web.Script.Serialization.ScriptIgnoreAttribute[0]}
However, Property is defined as a class property like this
public virtual Lot Lot { get; set; }
There is no [ScriptIgnore] attribute attached. Moreover, when I have tried this on Property when it was defined like this
[ScriptIgnore]
public virtual ICollection<Lot> Lots { get; set; }
I get back the same result as above
annotes | {System.Web.Script.Serialization.ScriptIgnoreAttribute[0]}
How can I use reflection to determine if an attribute exists? Or other means if possible, I also tried
var attri = Property.Attributes;
but it did not contain any attributes.
The following code works:
using System.Web.Script.Serialization;
public class TestAttribute
{
[ScriptIgnore]
public string SomeProperty1 { get; set; }
public string SomeProperty2 { get; set; }
public string SomeProperty3 { get; set; }
[ScriptIgnore]
public string SomeProperty4 { get; set; }
}
Define a static extension:
public static class AttributeExtension
{
public static bool HasAttribute(this PropertyInfo target, Type attribType)
{
var attribs = target.GetCustomAttributes(attribType, false);
return attribs.Length > 0;
}
}
Put the following sample code into a method and it picks up the attribute correctly - including ICollection by the way:
var test = new TestAttribute();
var props = (typeof (TestAttribute)).GetProperties();
foreach (var p in props)
{
if (p.HasAttribute(typeof(ScriptIgnoreAttribute)))
{
Console.WriteLine("{0} : {1}", p.Name, attribs[0].ToString());
}
}
Console.ReadLine();
NOTE: if you're using EF dynamic proxy classes, I think you will need to use ObjectContext.GetObjectType() to resolve to the original class before you can get the attributes, since the EF-generated proxy class will not inherit the attributes.
var props = type.GetProperties()
.Where(p => p.GetCustomAttributes().Any(a => a is ScriptIgnoreAttribute))
.ToList();

Categories

Resources