I am using a database first approach and have a class SalesWallboard mapped to the database table.
SalesWallboard.cs:
namespace Wallboards.DAL.SchnellDb
{
public partial class SalesWallboard
{
public int Id { get; set; }
public string StaffName { get; set; }
...
}
}
I have created a MetadataType class for this to apply custom attributes
SalesWallboardModel.cs
namespace Wallboards.DAL.SchnellDb
{
[MetadataType (typeof(SalesWallboardModel))]
public partial class SalesWallboard
{
}
public class SalesWallboardModel
{
[UpdateFromEclipse]
public string StaffName { get; set; }
...
}
}
But in my code, when I check it using Attribute.IsDefined, it always throws false.
Code
var salesWallboardColumns = typeof(SalesWallboard).GetProperties();
foreach (var column in salesWallboardColumns)
{
if (Attribute.IsDefined(column, typeof(UpdateFromEclipse))) //returns false
{
...
}
}
I have checked the answer given here. But I don't understand the example.
Can someone please simplify this for me?
I would like to thank #elgonzo and #thehennyy for their comments and correcting my understanding with reflection.
I found what I was looking for here. But I had to make a few modifications shown below.
I created a method PropertyHasAttribute
private static bool PropertyHasAttribute<T>(string propertyName, Type attributeType)
{
MetadataTypeAttribute att = (MetadataTypeAttribute)Attribute.GetCustomAttribute(typeof(T), typeof(MetadataTypeAttribute));
if (att != null)
{
foreach (var prop in GetType(att.MetadataClassType.UnderlyingSystemType.FullName).GetProperties())
{
if (propertyName.ToLower() == prop.Name.ToLower() && Attribute.IsDefined(prop, attributeType))
return true;
}
}
return false;
}
I also got help from here because my type was in a different assembly.
Method GetType
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
return type;
}
return null;
}
Then used it in my code like so
Code
var salesWallboardColumns = typeof(SalesWallboard).GetProperties();
foreach (var column in salesWallboardColumns)
{
var columnName = column.Name;
if (PropertyHasAttribute<SalesWallboard>(columnName, typeof(UpdateFromEclipse)))
{
...
}
}
Related
I am making an automatic saving and loading system that uses attributes. Inside my attribute, I have a method called SetSpecificField, which is supposed to do exactly that. It looks like this:
public static bool SetSpecificField(string className, string variableName, AutoLType autoLType)
{
bool worked = false;
try
{
worked = true;
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
if(type.GetCustomAttribute<AutoLoadEnabled>(true) != null)
{
if(type.Name == className)
{
foreach (PropertyInfo prop in type.GetProperties())
{
if (prop.Name == variableName)
{
prop.SetValue(autoLType.Value.GetType(), autoLType.Value);
}
}
foreach(FieldInfo field in type.GetFields())
{
if(field.Name == variableName)
{
field.SetValue(autoLType.Value.GetType(), autoLType.Value)
}
}
}
}
}
}
catch (Exception ex)
{
worked = false;
}
return worked;
}
However it is giving me this error: ArgumentException: Field aloadBeg defined on type SaveManager is not a field on the target object which is of type System.RuntimeType. I dont know if I am doing this right. If I am doing this wrong can someone show me how to do it properly?
EDIT:
Here is the AutoLType class:
public class AutoLType
{
public static implicit operator AutoSType(AutoLType l)
{
if (l.PropertyOrField == PropOrField.Field)
{
return new AutoSType(l.Name, l.Value, l.Attribute, AutoSType.PropOrField.Field);
}
else
{
return new AutoSType(l.Name, l.Value, l.Attribute, AutoSType.PropOrField.Prop);
}
}
public enum PropOrField
{
Prop,
Field
}
public string Name { get; set; }
public object Value { get; set; }
public AutoLoad Attribute { get; set; }
public PropOrField PropertyOrField { get; set; }
public AutoLType(string name, object value, AutoLoad attr, PropOrField propOrField)
{
Name = name;
Value = value;
Attribute = attr;
PropertyOrField = propOrField;
}
}
I have two different classes. And, those two different classes have multiple properties as well. Consider the following two example classes,
public Class1{
public string Key;
public string value;
}
public Class2{
public string Key;
public string value;
}
Note: For example, I added the class like above. But, in reality, the two classes should have different values with the same name.
These classes should be a member of the list like below,
List<Class1> list1 = new List<Class1>();
List<Class2> list2 = new List<Class2>();
So, to process these list I need a two different functions something like below,
private string GetStrValue(List<Class1> values)
{
string toRet = string.Empty;
if (values == null)
return toRet;
foreach (Class1 val in values)
{
if (!string.IsNullOrWhiteSpace(val.Key)) {
toRet = val.value;
break;
}
}
return toRet;
}
And, the similar function to process the Int class as well. So, I planned to use the generic. I have written the code like below,
private string GetValue<T>(List<T> listValue)
{
string toRet = string.Empty;
if (listValue == null)
return toRet;
foreach (T iter in listValue)
{
if (!string.IsNullOrWhiteSpace(iter.Key)) {
toRet = val.Name;
break;
}
}
return toRet;
}
But, the code does not compile. I'm facing the below error.
Severity Code Description Project File Line Suppression State
Error CS1061 'T' does not contain a definition for 'Name' and no accessible extension method 'Name' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
It would be much-appreciated anyone helping on this.
Thank you,
You have said "This code works for every type T" and yet you expect your Type T to have a property called Name, which many many types do not have. Generics do not work that way.
If you want to do something with your instance of type T that requires it to have certain properties, you need to tell the compiler that T is constrained to types that have those properties.
In your case you will need to write an interface common to both of your classes and the add that interface to your generic T definition.
This is well explained (including a good example with code) in the Microsoft documentation here
You have 2 options interface or father class. Bot 2 ways require where T: interfaceName or where T: fatherClassName syntax
For example with interface:
public interface IClass
{
string Key { get; set; }
string Name { get; set; }
string value { get; set; }
}
public class Class1 : IClass
{
public string Key { get; set; }
public string value { get; set; }
public string Name { get; set; }
}
public class Class2 : IClass
{
public string Key { get; set; }
public string value { get; set; }
public string Name { get; set; }
}
Then your generic class would be
private string GetValue<T>(List<T> listValue) where T : IClass
{
string toRet = string.Empty;
if (listValue == null)
return toRet;
foreach (T val in listValue)
{
if (!string.IsNullOrWhiteSpace(val.Key))
{
toRet = val.Name;
break;
}
}
return toRet;
}
You can make it completely generic using Reflection (refer Reflection | PropertyInfo). This way you would be able to handle any classes coming to you. Please refer to the sample code below:
private string GetValue<T>(List<T> listValue)
{
string toRet = string.Empty;
if (listValue == null)
return toRet;
PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
//Since you have mentioned all classes will have "Key" and "Value" and you need to use that only
//To make it completely generic you can maybe get this as input to this function
PropertyInfo keyProperty = properties.FirstOrDefault(x => x.Name.Equals("Key", StringComparison.OrdinalIgnoreCase));
PropertyInfo valueProperty = properties.FirstOrDefault(x => x.Name.Equals("Value", StringComparison.OrdinalIgnoreCase));
if (keyProperty == null || valueProperty == null)
return toRet;
foreach (T iter in listValue)
{
var keyData = keyProperty.GetValue(iter, null);
if (keyData != null && !string.IsNullOrWhiteSpace(keyData.ToString()))
{
toRet = valueProperty.GetValue(iter, null).ToString();
break;
}
}
return toRet;
}
As nvoigt mentioned, in this situation we have to use 'Interface' concept.
Define your classes and interface as below:
public interface IKeyValue
{
public string Key { get; set; }
public string Value { get; set; }
}
public class A : IKeyValue
{
public string Key { get; set; }
public string Value { get; set; }
//other properties...
}
public class B : IKeyValue
{
public string Key { get; set; }
public string Value { get; set; }
//other properties...
}
And your method which is going to use 'Key' and 'Value' should be like this:
private string GetValue<T>(List<T> listValue) where T: IKeyValue
{
string toRet = string.Empty;
if (listValue == null)
return toRet;
foreach (T iter in listValue)
{
if (!string.IsNullOrWhiteSpace(iter.Key))
{
toRet = iter.Value;
break;
}
}
return toRet;
}
'where T: IKeyValue' in method definition means type T is 'IKeyValue' that cause I can access the 'Key' and 'Value' in context (just those that are in IKeyValue interface)
This is how you can use it:
List<IKeyValue> keyValues = new List<IKeyValue>
{new A{Key="a",Value="b"}, new B{Key="x",Value="y"}};
List<A> listA = new List<A>
{ new A { Key = "h", Value = "b" }, new A { Key = "u", Value = "m" } };
List<B> listB = new List<B>
{ new B { Key = "h", Value = "b" }, new B { Key = "u", Value = "m" } };
string resultListInterface = GetValue(keyValues); //valid
string resultListA = GetValue(listA); //valid
string resultListB = GetValue(listB); //valid
For naming convention I change property name from 'value' to 'Value'
So I am using reflection to loop through the properties of one object and populating the values on a different object with properties of the same name. This works great but the problem comes when the property type is a collection. I want to be able to loop through each of the objects in the source collection and populate the same list with objects in the source collection.
public class SourceMessage
{
public string Name { get; set; }
public int Version { get; set; }
public IList<ValueDefinition> Values { get; set; }
}
public class ValueDefinition
{
public string Name { get; set; }
public string Value { get; set; }
}
public class TargetObject
{
public TargetObject()
{
Values = new List<TargetValueDefinition>();
}
public string Name { get; set; }
public int Version { get; set; }
public IList<TargetValueDefinition> Values { get; set; }
}
public class TargetValueDefinition
{
public string Name { get; set; }
public string Value { get; set; }
}
Then I use Reflection to populate the target from the source.
public static void PopulateFromMessage<T, TS>(ref T targetEntity, TS message)
{
var sourceType = typeof(TS);
var targetType = typeof(T);
foreach (var targetPropInfo in targetType.GetProperties())
{
if (sourceType.GetProperty(targetPropInfo.Name) != null)
{
var obj = sourceType.GetProperty(targetPropInfo.Name);
if (obj.PropertyType.Namespace == "System.Collections.Generic")
{
//var x = targetType.GetProperty(targetPropInfo.Name);
//PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
continue;
}
targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
}
}
}
So calling this would be like this:
private void DenormalizeMessage(SourceMessage message)
{
var newTargetObject = new TargetObject();
PopulateFromMessage(ref newTargetObject , message);
}
I can identify when the property is a collection but am uncertain of how to create new TargetValueDefinitions and populate them with the values from ValueDefinitions. In the end it is pretty much a copy of the SourceMessage in the form of a TargetObject.
This all stems from receiving messages and transforming them into objects with the same property names.
If your problem is iterating through items contained inside a single property when it is a collection, then the key would be to read the property value into a dynamic variable and not an object variable that is by default, this way you could use a foreach for it.
dynamic propVal = inputProperty.GetValue(item);
foreach (var subItem in propVal)
{
//do your stuff
}
Disclaimer: This is extremely unsafe to do and makes a lot of assumptions but it should puth you on the right path.
Change you method to this:
public static void PopulateFromMessage<T, TS>(T targetEntity, TS message)
{
var sourceType = typeof (TS);
var targetType = typeof (T);
foreach (var targetPropInfo in targetType.GetProperties())
{
if (targetPropInfo.PropertyType.IsGenericType)
{
if (targetPropInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
{
var originalList = sourceType.GetProperty(targetPropInfo.Name).GetValue(message) as IList;
if (originalList != null)
{
var argumentType = targetPropInfo.PropertyType.GetGenericArguments();
var listType = typeof (List<>);
var concreteType = listType.MakeGenericType(argumentType);
var newList = Activator.CreateInstance(concreteType) as IList;
foreach (var original in originalList)
{
var targetValue = Activator.CreateInstance(argumentType[0]);
// do this yourself. Here we're converting ValueDefinition to TargetValueDefinition
// targetValue.Fill(original);
}
targetPropInfo.SetValue(targetEntity, newList);
}
}
}
else
{
if (sourceType.GetProperty(targetPropInfo.Name) != null)
{
var obj = sourceType.GetProperty(targetPropInfo.Name);
if (obj.PropertyType.Namespace == "System.Collections.Generic")
{
//var x = targetType.GetProperty(targetPropInfo.Name);
//PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
continue;
}
targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
}
}
}
}
You should create a interface for each class (implement the methods and properties on interface) and implement it in each class. After, in function PopulateFromMessage should specify the interface allowed in method, with this you can use directly the properties of class with T and TS generic types.
I've been searching for a while now and tested several methods, but i didn't find the answer i was looking for. I'll try to explain.
I have an object with several fields/properties. These properties have custom attributes.
What i want is to get the custom attribute from a specific propertie without all the knowlege of the object.
The are the base classes
// FieldAttr has a public Text propery
public class TestObject
{
// Declare fields
[FieldAttr("prop_testfld1")]
public FLDtype1 testfld1 = new FLDtype1();
[FieldAttr("prop_testfld2")]
public FLDtype2 testfld2 = new FLDtype2();
[FieldAttr("prop_testfld3")]
public FLDtype1 testfld3;
}
public class FLDtype1
{
public string Value { get; set; }
}
public class FLDtype2
{
public Guid Value { get; set; }
}
public sealed class FieldAttr: System.Attribute
{
private string _txt;
public EntityFieldType(string txt)
{
this._text = txt;
}
public string Text { get { return this._text; } }
}
And i want to be able to do this in my application:
static void Main(string[] args)
{
TestObject test = new TestObject();
// (Option 1: preferred)
Console.WriteLine(test.testfld1.getFieldAttr().Text);
// (Option 2)
Console.WriteLine(test.getFieldAttr(test.testfld1).Text);
}
Is this possible? I've seen methods to get custom attribute values from all properties/fields of an object, but not for a specific field.
I've got a working method to get custom attribute from an enum, but wasn't able to recreate it for object fields/properties. This is because i couldn't get the name of the field i was trying to explore, because (for example) test.testfld1.ToString() give's me "ns.FLDtype1".
Looking forward for the answer :)
(and excuse my english)
Yes it is possible:
public static class Extensions
{
public static FieldAttr GetFieldAttr(
this TestObject source,
Expression<Func<TestObject,object>> field)
{
var member = field.Body as MemberExpression;
if (member == null) return null; // or throw exception
var fieldName = member.Member.Name;
var test = typeof (TestObject);
var fieldType = test.GetField(fieldName);
if (fieldType != null)
{
var attribute = fieldType.GetCustomAttribute<FieldAttr>();
return attribute;
}
return null;
}
}
Usage:
TestObject test = new TestObject();
var attr = test.GetFieldAttr(x => x.testfld3);
if(attr != null) Console.WriteLine(attr.Text);
Here is the fiddle
After another day of trial and error I decided to make use of Selman22 answer with a little modification. This is code I created:
public class TestObject : iTestObject
{
// Declare fields
[FieldAttr("prop_testfld1")]
public FLDtype1 testfld1 = new FLDtype1();
[FieldAttr("prop_testfld2")]
public FLDtype2 testfld2 = new FLDtype2();
[FieldAttr("prop_testfld3")]
public FLDtype1 testfld3;
}
public class FLDtype1 : iField
{
public string Value { get; set; }
}
public class FLDtype2 : iField
{
public Guid Value { get; set; }
}
public sealed class FieldAttr: System.Attribute
{
private string _txt;
public FieldAttr(string txt)
{
this._txt = txt;
}
public string Text { get { return this._txt; } }
}
public interface iField { }
public interface iTestObject { }
public static class Extensions
{
public static FieldAttr GetFieldAttr<T>(this T source, Expression<Func<iField>> field) where T : iTestObject
{
// Get member body. If no body present, return null
MemberExpression member = (MemberExpression)field.Body;
if (member == null) { return null; }
// Get field info. If no field info present, return null
FieldInfo fieldType = typeof(T).GetField(member.Member.Name);
if (fieldType == null) { return null; }
// Return custom attribute
return fieldType.GetCustomAttribute<FieldAttr>();
}
}
Usage:
public class Program
{
public static void Main()
{
TestObject test = new TestObject();
Console.WriteLine(test.GetFieldAttr(() => test.testfld1).Text);
Console.WriteLine(test.GetFieldAttr(() => test.testfld2).Text);
Console.WriteLine(test.GetFieldAttr(() => test.testfld3).Text);
}
}
Uses:
using System;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
I have implemented interfaces to protect the GetFieldAttr method
#Sulman22: Thnx for the response!
I have a view model which uses custom attributes such as
public int Id { get; set; }
public string Name { get; set; }
[IsEnumeration(typeof(CaseStatus))]
public string Status { get; set; }
IsEnumeration is a custom attribute which takes an Enumeration superclass as a parameter (actually it takes any type, but that doesn't matter since noone else will be using this)
public class IsEnumerationAttribute : Attribute
{
public Type Enumeration;
public IsEnumerationAttribute(Type enumeration)
{
Enumeration = enumeration;
}
}
What I want is to be able to get the type specified for any parameter. Currently my code looks like this:
public T EnumerationValuesToDisplayNames<T>(T item) where T : new()
{
if (LoggedInUser.IsSuper) return item;
var tProps = typeof (T).GetProperties()
.Where(prop => Attribute
.IsDefined(prop, typeof (IsEnumerationAttribute)));
foreach (var prop in tProps)
{
if (prop.GetValue(item, null) != null)
{
/*
Here I look through all properties with the IsEnumerable attribute.
I want to do something such as:
var type = prop.GetAttribute(item, typeof(IsEnumerable));
var displayName = Enumeration<type>.FromId(prop.GetValue(item, null));
prop.SetValue(item, displayName);
*/
}
}
return item;
}
I hope this makes sense, any help would be greatly appreciated, thanks
Assuming from your post you have a class defined as such:
public class Enumeration<T> {
public static string FromId(string id) {
// FromId Implmentation
}
}
Then you should just need
foreach (var prop in tProps) {
var id=prop.GetValue(item, null);
if (id!=null) {
var type = prop.GetCustomAttributes(typeof(EnumerationAttribute>,true).OfType<EnumerationAttribute>().Select(x=>x.Enumeration).First();
var enumerationType=typeof(Enumeration<>).MakeGenericType(type);
var fromIdMethod=enumerationType.GetMethod("FromId",BindingFlags.Public|BindingFlags.Static|BindingFlags.InvokeMethod);
var displayName=fromIdMethod.Invoke(null,new object[] {id});
prop.SetValue(item, displayName);
}
}
Alternatively you could implement the FromId method directly in the EnumerationAttribute then you could just call it directly like so...
foreach (var prop in tProps) {
var id=prop.GetValue(item, null);
if (id!=null) {
var enumAttrib = prop.GetCustomAttributes(typeof(EnumerationAttribute>,true).OfType<EnumerationAttribute>().First();
var displayName=enumAttrib.FromId((string)id);
prop.SetValue(item, displayName);
}