I have this POCO class with properties that use a custom attribute:
Application status flags POCO class
public class ApplicationStatusFlags
{
public int ApplicationId { get; set; }
[SectionFlag("APPLICANTPERSONALDETAILS")]
public bool PersonalDetailsStatus { get; set; }
[SectionFlag("APPLICANTECREGISTRATION")]
public bool EcRegistrationStatus { get; set; }
[SectionFlag("APPLICANTCV")]
public bool CvUpload { get; set; }
[SectionFlag("APPLICANTSTATEMENT")]
public bool IceAttributeStatement { get; set; }
[SectionFlag("APPLICANTCPD")]
public bool CpdUpload { get; set; }
[SectionFlag("APPLICANTORGCHART")]
public bool OrgChartUpload { get; set; }
[SectionFlag("APPLICANTSPONSORDETAILS")]
public bool SponsorDetails { get; set; }
}
Section flag attribute class
[AttributeUsage(AttributeTargets.All)]
public class SectionFlagAttribute : Attribute
{
/// <summary>
/// This constructor takes name of attribute
/// </summary>
/// <param name="name"></param>
public SectionFlagAttribute(string name)
{
Name = name;
}
public virtual string Name { get; }
}
I'm trying to get the value of one of these properties by using a string with the section flag name.
So if var foo = "APPLICANTSPONSORDETAILS" I would get the boolean value of SponsorDetails.
Sample code
updateAppStatusFlag.ApplicationId = applicationId;
var applicationStatuses =
await _applicationService
.UpdateApplicationStatusFlagsAsync<ApplicationStatusFlags>(updateAppStatusFlag);
var foo = "APPLICANTSPONSORDETAILS";
var type = applicationStatuses.GetType();
var test = type.
GetCustomAttributes(false)
.OfType<SectionFlagAttribute>()
.SingleOrDefault()
?.Name == foo;
Any ideas how to do this? I know I can use reflection but I've had problems getting it to work.
Thanks
In your example, you're getting the customattributes of the class instead of the properties.
Here is an example:
private object GetValueBySectionFlag(object obj, string flagName)
{
// get the type:
var objType = obj.GetType();
// iterate the properties
var prop = (from property in objType.GetProperties()
// iterate it's attributes
from attrib in property.GetCustomAttributes(typeof(SectionFlagAttribute), false).Cast<SectionFlagAttribute>()
// filter on the name
where attrib.Name == flagName
// select the propertyInfo
select property).FirstOrDefault();
// use the propertyinfo to get the instance->property value
return prop?.GetValue(obj);
}
Note: this will return only the first property which contains the SectionFlagAttribute with the right name. You could modify the method to return multiple values. (like a collection of propertyname/value)
Usage:
// a test instance.
var obj = new ApplicationStatusFlags { IceAttributeStatement = true };
// get the value by SectionFlag name
var iceAttributeStatement = GetValueBySectionFlag(obj, "APPLICANTSTATEMENT");
If the returned value is null then the flag is not found or the property's value is null.
Related
I want to write a method that should return the value from an object for the specified property.
public class MyClass
{
public int a { get; set; }
public int b { get; set; }
public int c { get; set; }
public int d { get; set; }
}
public int GetValue(string field)
{
MyClass obj=new MyClass();
obj=FromDb(); //get value from db
dynamic temp=obj;
return temp?.field;
}
The above code is only to demonstrate to what I am looking for.
Here I want to pass the property name (i.e a/b/c/d as per my above code) as an input to the method GetValue and it should return the value of that property.
This code is compiling successfully, but in run time it will search for the property name field not the value of field variable.
Any suggestions or workaround will be appreciated.
You can use reflection to get the value:
public int GetValue(string field)
{
MyClass obj = new MyClass();
obj = FromDb(); //get value from db
var property = obj.GetType().GetProperty(field);
return (int)property.GetValue(obj);
}
In my DB, all the tables have the status filed, but every table has a different name for that column. For e.g. the user table has user_status and the branch table has branch_status. All these columns of different tables would be having the same value. I have created POCO entities for all and wanted to create a generic function that would perform a query on the status field of the specified POCO entity class. So I wanted to create an attribute stating that this would be the status field of the specified entity class.
So, attribute that will give me the property name on which it is declared. The attribute would be declared only on a single property in the class. Till now I have done the following and it is working but wanted to know the efficient way to achieve the same. Below is my code:
Custom Attribute:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class StatusFieldAttribute : Attribute
{
}
Declaration
public class UserTable
{
public int UserId{ get; set; }
[StatusField]
public int UserStatus { get; set; }
}
public class BranchTable
{
public int BranchId{ get; set; }
[StatusField]
public int BranchStatus { get; set; }
}
I want to get the property name from UserTable and BranchTable having the StatusField Attribute. I have achieve the same as:
Type type = typeof(UserTable);
string statusFieldName = type.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(StatusFieldAttribute))).FirstOrDefault().Name;
And the above code gives the proper output as UserStatus. But is there an efficient way to achieve the same using something like below:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class StatusFieldAttribute : Attribute
{
public StatusFieldAttribute([CallerMemberName] string propertyName = null)
{
StatusFieldName = propertyName;
}
public string StatusFieldName { get; }
}
And access the StatusFieldName to get the name of the property on which the StatusField attribute is declared in a particular class.
One approach is to define a marker interface (prior to C# 8), and define an extension method to retrieve the proper status field, like so:
interface IStatusFieldMarker
{
}
static class Extensions
{
public static string GetStatusFieldName(this IStatusFieldMarker t) =>
t.GetType()
.GetProperties()
.Single(p => Attribute.IsDefined(p, typeof(StatusFieldAttributeAttribute)))
.Name;
// optional getter/setter
public static int GetStatus(this IStatusFieldMarker t) =>
(int)(t.GetType()
.GetProperty(GetStatusFieldName(t))
.GetValue(t));
public static void SetStatus(this IStatusFieldMarker t, int value)
{
t.GetType()
.GetProperty(GetStatusFieldName(t))
.SetValue(t, value);
}
}
Then mark the POCO objects with the interface:
class Branch : IStatusFieldMarker
{
[StatusFieldAttribute]
public int BranchStatus { get; set; }
}
class User : IStatusFieldMarker
{
[StatusFieldAttribute]
public int UserStatus { get; set; }
}
You can then call the GetStatusFieldName extension method to get the field name:
Branch b = new Branch() { BranchStatus = 3 };
User u = new User() { UserStatus = 1 };
Console.WriteLine(b.GetStatusFieldName()); // prints BranchStatus
Console.WriteLine(u.GetStatusFieldName()); // prints UserStatus
Console.WriteLine(b.GetStatus()); // prints 3
Console.WriteLine(u.GetStatus()); // prints 1
I simply created below function to get the status name
public static string GetStatusField<T>(this T type) where T : Type
{
PropertyInfo propertyInfo = type.GetProperties().FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(StatusFieldAttribute)));
if (propertyInfo == null)
{
Log.Error($"StatusFieldAttribute not found in {type.Name} ");
throw new Exception($"StatusFieldAttribute not found in { type.Name } ");
}
else
return propertyInfo.Name;
}
And invoked the function as
Type type = typeof(UserTable);
string statusFieldName = type.GetStatusField();
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!
I have the following object:
public class ContactImport {
public long Id {get;set;}
public Contact Contact {get;set;}
public Company Company {get;set;}
public Address Address {get;set;}
}
I want to be able to set the properties on the the nested objects dynamically based on an expression passed in (Expression<Func<T, dynamic>>).
To do this I have the following method, this works fine for setting properties on the top level object (such as Id) but fails when trying to set anything on the nested objects (such as Contact.FirstName)
public static void SetPropertyValue<T, TProp>(this T target, Expression<Func<T, TProp>> member, TProp value) {
var selectorExpr = (MemberExpression)(member.Body is UnaryExpression ? ((UnaryExpression)member.Body).Operand : member.Body);
if (selectorExpr != null) {
var property = selectorExpr.Member as PropertyInfo;
if (property != null) {
property.SetValue(target, value);
}
}
}
It looks like it's trying to set the property on the top level object but can't. I take it this is possible but I'm unsure how to achieve it with what I currently have.
The SetPropertyValue method is invoke like this:
public class ImportCheck<T> {
public int id { get; set; }
public string Name { get; set; }
public Type Type { get; set; }
public bool Required { get; set; }
public int? MinLength { get; set; }
public int? MaxLength { get; set; }
public string Value { get; set; }
public Expression<Func<T, dynamic>> AssociatedProperty { get; set; }
}
T pt = (T)Activator.CreateInstance(typeof(T));
foreach (var m in mapping) {
pt.SetPropertyValue(m.AssociatedProperty, Convert.ChangeType(m.Value, Nullable.GetUnderlyingType(m.Type) ?? m.Type));
}
In the above example T is ContactImport, m.AssociatedProperty is the expression and mapping is a List<ImportCheck<ContactImport>>.
Update
My issue seems to boil down to the fact that the expression being passed into SetPropertyValue has a return type of dynamic. If this is set to int and the property on the nested object is an int then it all works. The problem I have then is that I need to explicitly set the result of the expression to match the type of the target property. This however leaves me with another issue in that I need a list of ImportChecks with the possibility of each Expression<Func<T,dynamic>> having a different return type.
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);