I want initialize properties of my class with some data from file. I created DataSourceAttribute for classes and DataItemAttribute for properties. I also created abstract parent class for these classes for which I want initialize fields. See code below:
[AttributeUsage(AttributeTargets.Class)]
public class DataInitializerAttribute: Attribute
{}
[AttributeUsage(AttributeTargets.Property)]
public class DataItemAttribute: Attribute
{
public string FileName { get; set; }
public string Field { get; set; }
public string Value
{
get { return "someValue"; // here I will read file}
}
public DataItemAttribute(string fileName)
{
FileName = fileName;
}
}
public abstract class PropertyInitializer
{
protected PropertyInitializer()
{
var classAttribute = GetType().GetCustomAttribute<DataInitializerAttribute>();
if (classAttribute == null)
{
return;
}
var properties = GetType().GetProperties();
foreach (var property in properties)
{
var attribute = property.GetCustomAttribute<DataItemAttribute>();
if (attribute == null)
{
continue;
}
property.SetValue(this, attribute.Value);
}
}
}
Everything works good, but maybe it is possible to implement more elegant solution without inheriting of PropertyInitializer class?
Related
I have some classes which inherit from the Attribute class. These Attributes (like [Table] and [Column] are used to mark classes and properties as database tables or database columns and let me generate a database schema at runtime. Right now they are not only used as "markers", they represent the schema itself. For example the Table class does not only inherit from Attribute but also contains a property where a list of columns of the table are stored.
public class Schema : Attribute
{
public List<Table> Tables { get; set; } = new List<Table>();
}
public class Table : Attribute
{
public string Name { get; set; }
public List<Column> Columns { get; set; } = new List<Column>();
}
public class Column : Attribute
{
public string Name { get; set; }
}
[Schema] public class Example
{
[Table] public class Person
{
[Column] public static string Name { get; set; } => "name";
[Column] public static string Adress { get; set; } => "adress";
}
}
public class Test
{
public static Schema GenerateSchema<T>()
{
var schemaAttribute = (typeof T).GetCustomAttribute<Schema>();
if (schema == null)
{
return null;
}
foreach (var type in typeof(T).GetNestedTypes())
{
if (Attribute.IsDefined(type, typeof(Table)))
{
var tableAttribute = type.GetCustomAttribute<Table>();
var properties = type.GetProperties();
var columnAttributes = new List<Column>();
foreach (var property in properties)
{
var columnAttribute = property.GetCustomAttribute<Column>();
if (columnAttribute != null)
{
columnAttribute.Name = property.GetValue(null) as string;
columnAttributes.Add(columnAttribute);
}
}
tableAttribute.Columns = columnAttributes;
schemaAttribute.tables.Add(tableAttribute);
}
return schemaAttribute;
}
}
}
Is there any drawback in using the attribute classes to not only mark the classes and properties but also hold the informations like shown above? Or might it be a "better" idea to use seperate classes for the schema, tables and columns and only use the attribute classes as "markers"?
I have the below Model class,
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
public class FormField
{
[Required]
[JsonPropertyName("STD_USERTYPEID")]
public string UserTypeId { get; set; }
[Required]
[JsonPropertyName("STD_OFFICETYPEID")]
public string OfficeTypeId { get; set; }
}
I have a few scenarios where STD_OFFICETYPEID may come as LegacyOFFICETYPEID or OfficeID. Is there a way in which I can dynamically generate JsonPropertyName?
I am using System.Text.Json NuGet package.
Note that this example is simplified. In my production code there could be 20-25 concrete properties. And all of these properties could map to 5-10 different JsonPropertyNames each.
I'll tell you how I would do this: source generators.
First I would inject a new attribute, JsonPropertyNames(params string[] alternativeNames), and I'd decorate my class with it instead, giving it the full list of possible field names.
Then I'd have the source generator generate a second class matching properties with my original class, including properties for each of the alternative names provided using JsonPropertyNames. This is the class you'd be reading the Json into, and all your properties would get read in one of the properties.
Then the generator would add all the necessary AutoMapper mapping code to copy from my generated type to the original type, as well as a helper class that reads into the generated type and invokes AutoMapper to return the class for you.
So from the caller side, you'd just need to call one function to get your type, ignoring all the details behind the scenes.
if you are ready to swith to Newtonsoft.Json you can try this code
var json="{\"STD_USERTYPEID\":\"userId\",\"LegacyOFFICETYPEID\":\"officeId\"}";
FormField formField = DeserializeObj<FormField>(json);
public class FormField
{
[JsonProperty("STD_USERTYPEID")]
public string UserTypeId { get; set; }
[JsonPropertyNames(new string[] {"STD_OFFICETYPEID", "LegacyOFFICETYPEID" })]
public string OfficeTypeId { get; set; }
}
public T DeserializeObj<T>(string json) where T:new()
{
var jsonObj = JObject.FromObject(new T());
var attrs = GetAttrs<T>();
var jsonParsed = JObject.Parse(json);
foreach (var prop in jsonParsed.Properties())
{
var propName=prop.Name;
var attr=attrs.Where(a=>a.AttributeNames.Contains(propName)).FirstOrDefault();
if(attr!=null) jsonObj[attr.PropertyName]=prop.Value;
else jsonObj[propName]=prop.Value;
}
return jsonObj.ToObject<T>();
}
public static List<PropertyAttributes> GetAttrs<T>() where T: new()
{
var source= new T();
var attrs = new List<PropertyAttributes>();
foreach (PropertyInfo prop in source.GetType().GetProperties())
{
var attribute = prop.GetCustomAttribute<JsonPropertyNamesAttribute>();
if (attribute != null)
{
attrs.Add(new PropertyAttributes { PropertyName = prop.Name, AttributeNames = attribute.Names });
}
}
if (attrs.Count > 0) return attrs;
return null;
}
public class PropertyAttributes
{
public string PropertyName { get; set; }
public string[] AttributeNames { get; set; }
}
[AttributeUsage(AttributeTargets.All)]
public class JsonPropertyNamesAttribute : Attribute
{
private string[] names;
public JsonPropertyNamesAttribute(string[] names)
{
this.names = names;
}
public virtual string[] Names
{
get { return names; }
}
}
I have a method which takes an object as a parameter. Within that method I walk through that objects properties with reflection. Some properties are of a generic class type. I like to read a property of that generic class property, but I cannot cast it to a generic class.
public abstract class BaseClass
{
public int Id { get; set; }
}
public abstract class GenericClass<T>: BaseClass
{
public string Description { get; set; }
}
public class DerivedClassA: GenericClass<DerivedClassA>
{
public string A { get; set; }
}
public class DerivedClassB: GenericClass<DerivedClassB>
{
public string B { get; set; }
}
public class ReflectingClass: BaseClass
{
public string Code { get; set; }
public DerivedClassA DerA { get; set; }
public DerivedClassB DerB { get; set; }
}
public static void Reflecting(object obj)
{
var t = GetType(obj)
foreach (var pi in t.GetProperties())
{
if (obj.GetType().BaseType.GetGenericTypeDefinition() == typeof(GenericClass<>)
{
var genClassObjProperty = ((GenericClass<T>)obj).Description; // Error, cannot do this at all !!!
}
}
}
What I want is for the code to walk to the properties and whatever the derived class actually is get the Description property of the GenericClass it is derived from.
I am using a generic class, because elsewhere in the code I call methods by their derived class and get the proper class type without resorting to all kinds of cast and passing types. I.e:
DerivedClassA.DoSomething()
instead of
BaseClass.DoSomething<DerivedClassA>()
or
BaseClass.DoSomething(type derivedClassType)
Take a look at this:
public static void Reflecting(object obj)
{
foreach (var pi in obj.GetType().GetProperties())
{
if (pi.PropertyType.BaseType.IsGenericType
&& pi.PropertyType.BaseType.GetGenericTypeDefinition()
== typeof(GenericClass<>))
{
var propValue = pi.GetValue(obj);
if (propValue != null)
{
var description = propValue.GetType()
.GetProperty("Description").GetValue(propValue);
Console.WriteLine(description);
}
}
}
Console.ReadKey();
}
I think this is what you need.
My situation is the following:
I'm coding a MVC website on Visual Studio 2013 using database-first approach with Entity Framework.
EF automatically generates the models. But I need to add custom attributes (~NOT~ necessarily for data validation but also for internal processes) and, via reflection, access those custom attributes.
Let's say I have
public partial class Application {
public int AppID {get; set;}
public string Name {get; set;}
//etc...
}
I've tried the following approaches:
• On a different file I continue the partial class:
public partial class Application {
[MyAttributeOne]
public int AppID { get; set; }
[DataType(DataType.Text)]
[MyAttributeTwo]
public string Name { get; set; }
}
• Use the MetaData class
public class ApplicationMetadata {
[MyAttributeOne]
public int SolutionID { get; set; }
[DataType(DataType.Text)]
[MyAttributeTwo]
public string Name { get; set; }
}
[MetadataType(typeof(ApplicationMetadata))]
public partial class Application { }
• Inherit the class with attributes:
public class ApplicationMetadata {
[MyAttributeOne]
public int SolutionID { get; set; }
[DataType(DataType.Text)]
[MyAttributeTwo]
public string Name { get; set; }
}
public partial class Application : ApplicationMetadata { }
• And the 'Buddy class' approach where I do basically the previous 2 approaches but instead I define the class with the attributes inside the 'Application' class.
Am I doing something wrong? Or is this simply impossible?
I need to be able to make the following code work:
foreach (PropertyInfo propertyInfo in currentObject.GetType().GetProperties())
{
foreach (CustomAttributeData attrData in propertyInfo.GetCustomAttributesData())
{
if (typeof(attrData) == typeof(MyAttributeOne))
//stuff
else if (typeof(attrData) == typeof(MyAttributeTwo))
//different stuff
else
//yet more stuff
}
}
Thank you very much for the attention!
Regards.
OK, this is a little involved but it's fairly simple. This is also really a bit of a brain dump but it does work and gives you enough to work with. Lets set up with some basics:
//A couple of custom attributes
public class MyAttributeOne : Attribute { }
public class MyAttributeTwo : Attribute { }
//A metadata class where we can use the custom attributes
public sealed class MyEntityMetadata
{
//This property has the same name as the class it is referring to
[MyAttributeOne]
public int SomeProperty { get; set; }
}
//And an entity class where we use System.ComponentModel.DataAnnotations.MetadataType
//to tell our function where the metadata is stored
[MetadataType(typeof(MyEntityMetadata))]
public class MyEntity
{
public int SomeProperty { get; set; }
}
OK, still with me? Now we need a function to process the properties in the same way you did earlier:
public void DoStuff(object currentObject)
{
//Lets see if our entity class has associated metadata
var metaDataAttribute = currentObject.GetType()
.GetCustomAttributes()
.SingleOrDefault(a => a is MetadataTypeAttribute) as MetadataTypeAttribute;
PropertyInfo[] metaProperties = null;
//Cache the metadata properties here
if (metaDataAttribute != null)
{
metaProperties = metaDataAttribute.MetadataClassType.GetProperties();
}
//As before loop through each property...
foreach (PropertyInfo propertyInfo in currentObject.GetType().GetProperties())
{
//Refactored this out as it's called again later
ProcessAttributes(propertyInfo.GetCustomAttributes());
//Now check the metadata class
if (metaProperties != null)
{
//Look for a matching property in the metadata class
var metaPropertyInfo = metaProperties
.SingleOrDefault(p => p.Name == propertyInfo.Name);
if (metaPropertyInfo != null)
{
ProcessAttributes(metaPropertyInfo.GetCustomAttributes());
}
}
}
}
And of course, here is the refactored method to process the attributes:
private void ProcessAttributes(IEnumerable<Attribute> attributes)
{
foreach (var attr in attributes)
{
if (attr is MyAttributeOne)
{
Console.WriteLine("MyAttributeOne found");
}
else if (attr is MyAttributeTwo)
{
Console.WriteLine("MyAttributeTwo found");
}
else
{
}
}
}
Note: I'm asking about subclasses, not derived classes.
Basically, what I need to do is check properties of an object and look for those that have a specific attribute set.
The problem I have is that a lot of the properties are from subclasses
public class ExampleAttribute : Attribute
{
public object Whatever { get; set; }
}
public class MiddleEarth
{
[Example]
public Type EntityType { get; set; }
}
public class Elf : MiddleEarth
{
[Example]
public SubClass ItsLateAndImTired { get; set; }
public IList<Arg> Args { get; set; }
//Need to check properties of this object as well
public class SubClass
{
public object SubProperty { get; set; }
[Example]
public object SubPropertyWithAttribute { get; set; }
}
public class Arg
{
[Example]
public string Something { get; set; }
}
}
Now, I'm trying to do it as follows...but for reasons noted in the comments it won't work
public List<string> IterateProperties(object _o)
{
List<string> problems = new List<string>();
foreach (PropertyInfo info in _o.GetType().GetProperties())
{
//All 3 of these will return the exact same thing
Type thisType = this.GetType();
Type oType = _o.GetType();
Type infoType = info.ReflectedType;
//IsSubClassOf only checks for derived classes,
//so it's not the method I'm looking for
if (info.ReflectedType.IsSubclassOf(this.GetType()))
{
object sub = info.GetValue(_o, null);
if (sub != null)
{
problems.AddRange(this.IterateProperties(sub));
}
}
object[] attributes = info.GetCustomAttributes(typeof(ExampleAttribute), true);
foreach (object o in attributes)
{
if (info.GetValue(_o, null) == null)
{
problems.Add(String.Format("Attribute {0} in class {1} cannot be null", info.Name, info.ReflectedType.ToString()));
}
}
}
return problems;
}
Any ideas?
I believe what you're looking for is Type.GetNestedTypes()
http://msdn.microsoft.com/en-GB/library/493t6h7t.aspx
I'm not sure, but think that GetProperties method got some flags that can help...