I want to use CsvHelper.Configuration.ClassMap by dynamically assigned properties.
Usually you map a Property like this in a static manner: You have to assign each property and its 'text to display'.
using CsvHelper.Configuration;
public sealed class CleanSQLRowDescriptorMap : ClassMap<CleanSQLRowDescriptor>
{
public CleanSQLRowDescriptorMap()
{
Map(f => f.OriginalIndex).Name("Original Index");
Map(f => f.OriginalRow).Name("Original Row");
}
}
I want to do the following:
using CsvHelper.Configuration;
public sealed class CleanSQLRowDescriptorMap : ClassMap<CleanSQLRowDescriptor>
{
public CleanSQLRowDescriptorMap()
{
// Filter by attribute (implementation returns PropertyInfo List)
List<PropertyInfo> mappedProperties = CleanSQLRowDescriptor.Create().FilterPropertiesByAttribute();
// Dynamically assign each property and its assigned 'attribute value'
// At the moment I mapped the PropertyInfo.Name, but I actually need to use the Property as the static example above.
// Also need to figure out how to get the Attribute value (DisplayName in this example).
mappedProperties.ForEach(prop => Map(f => prop.Name).Name(prop.Name));
}
}
I currently have the following method used above:
[DisplayName("Original Index")]
public int OriginalIndex { get; set; }
[DisplayName("Original Row")]
public string OriginalRow { get; set; }
public string DonotWantToAssignThis { get; set; }
public List<PropertyInfo> FilterPropertiesByAttribute()
{
// This function already returns only the attributes that use
// [DisplayName] and other attributes defined for other properties,
// ignoring other properties that do not have any of these attributes.
return properties;
}
How can I use the PropertyInfo List of items to dynamically assign the ClassMap? I want to create a base class with these attributes as filters and all the classes implementing this base class would have the same capability, making it easier to 'maintain the mappings'.
I managed to figure it out, VS Code did not give me all the overloads for Map() function, so I missed overloads.
This one is used in all examples:
MemberMap<TClass, TMember> Map<TMember>(Expression<Func<TClass, TMember>> expression, bool useExistingMap = true);
I found this inside JoshClose/CSVHelper:
public MemberMap Map(Type classType, MemberInfo member, bool useExistingMap = true)
So instead of using 'Expression that requires the property name as TMember' which does not take the type I can now assign the MemberInfo directly.
The code below just shows a solution for a single attribute [DisplayName] by using its .DisplayName property value.
For additional Attributes like I have at the moment, I will need to handle the property value differently:
mappedProperties.ForEach(prop =>
{
Map(typeof(CleanSQLRowDescriptor), prop).Name(prop.GetCustomAttribute<DisplayNameAttribute>().DisplayName);
});
Related
I am working on an application that stores data in the ConfigurationManager.AppSettings file, and I am wanting to implement it in a different way than how I do right now. Currently, I have an interface (see below) that each class with saveable traits needs to implement, then call the static save methods from my Config class (example below). I don't like the coupling between my Config class and the class with the saveable data, so my ideal would be to have an attribute that indicates a property should be saved. Then, instead of calling the SaveData or LoadData functions in my manager class, I would call a function that sets/saves all the attributed properties. This seems similar to how [Serializeable] works in default C#, so I imagine it's possible somehow. However, most of my searches have been fruitless. Any ideas on how to implement something like this?
Interface
Example
Reflection is what you're looking for.
Reflection provides objects (of type Type) that describe assemblies, modules, and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. If you are using attributes in your code, reflection enables you to access them.
Assuming that you're only interested in properties, you can use typeof or GetType to get an instance of System.Type. You can then call GetProperties to get an IEnumerable<PropertyInfo>. PropertyInfo has an Attributes property that you can use to retrieve the attributes for that property. You can also use an instance of PropertyInfo to retrieve the value of the property.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
[AttributeUsage(AttributeTargets.Property)]
public class MyAttribute : Attribute
{
}
public class Foo
{
[My]
public string Bar { get; set; }
public string Baz { get; set; }
[My]
public string Id { get; set; }
}
public static class Utilities
{
public static IEnumerable<PropertyInfo> GetPropertiesWithMyAttribute(object obj)
{
return obj.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(pi => pi.CustomAttributes.Any(ca => ca.AttributeType == typeof(MyAttribute)));
}
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo()
{
Bar = "Bar_Value",
Baz = "Baz_Value",
Id = "Id_Value"
};
foreach (var pi in Utilities.GetPropertiesWithMyAttribute(foo))
{
Console.WriteLine($"{pi.Name}: {pi.GetMethod.Invoke(foo, null).ToString()}");
}
foreach (var pi in Utilities.GetPropertiesWithMyAttribute(foo))
{
pi.SetMethod.Invoke(foo, new object[] { $"{pi.Name}_Value_Reflection" });
}
Console.WriteLine(foo.Bar);
Console.WriteLine(foo.Baz);
Console.WriteLine(foo.Id);
Console.ReadKey();
}
}
Of course, this example only string properties. You're going to have to figure out some way to deal with properties that aren't strings; for example you haven an ObservableCollection in your example.
I need to mark some properties as passwords so that they could be automatically screened. I found a standard attribute for that:
[PasswordPropertyText]
public string ThePassword { get; set; }
Following method checks if the attribute is there:
private static bool _isPassword(PropertyInfo p)
{
PasswordPropertyTextAttribute passProp = (PasswordPropertyTextAttribute)p.GetCustomAttribute(typeof(PasswordPropertyTextAttribute));
return (passProp != null); // Additional condition should go here
}
Now I would like to have my own logic here:
[PasswordPropertyText] should result in true.
[PasswordPropertyText(true)] should result in true.
[PasswordPropertyText(false)] should result in false.
but the default value of PasswordPropertyTextAttribute.Password is false when the argument is omitted.
Is there any way to get the raw attribute value?
Since attribute constructor information are stored as metadata as well you can get the required information by calling the GetCustomAttributesData method. Have a look at this simple example:
class Program
{
[PasswordPropertyText]
public string Password1 { get; set; }
[PasswordPropertyText(true)]
public string Password2 { get; set; }
[PasswordPropertyText(false)]
public string Password3 { get; set; }
static void Main(string[] args)
{
var props = typeof(Program).GetProperties();
foreach(var prop in props)
{
var attributeData = prop.GetCustomAttributesData().First(x => x.AttributeType == typeof(PasswordPropertyTextAttribute));
Console.WriteLine($"{prop.Name}: {(attributeData.ConstructorArguments.Cast<CustomAttributeTypedArgument?>().FirstOrDefault()?.Value ?? true)}");
}
Console.ReadLine();
}
}
Output:
Password1: True
Password2: True
Password3: False
What you're describing cannot be done using reflection.
Reflection is not looking at the code as it was written: it is looking at the model that results from that code. Since the default constructor for PasswordPropertyTextAttribute passes a false value to another constructor, the model that it produces is indistinguishable from using [PasswordPropertyText(false)].
If you want different behavior from the built-in attribute, I'd recommend creating your own attribute that has the behavior you're looking for instead.
I'm currently trialing Entity Framework Core 2.1 with a view to using it in the company I work for's business applications. I've got most of the way in implementing Value Converters in my test project but my existing knowledge base has let me down at the last hurdle!
What I'm trying to do
My understanding is that for enum values, the built in type converters can convert from the enum value to the string equivalent (EnumToStringConverter) or from the enum value to it's numerical representation (EnumToNumberConverter). However we use a custom string value to represent the enum in our database, so I have written a custom EnumToDbStringEquivalentConvertor to do this conversion and the database string value is specified as an attribute on each of the enum values in my model.
The code is as follows:
Model
public class User
{
[Key] public int ID { get; set; }
public EmployeeType EmployeeType { get; set; }
}
public enum EmployeeType
{
[EnumDbStringValue("D")]
Director,
[EnumDbStringValue("W")]
Weekly,
[EnumDbStringValue("S")]
Salaried
}
DataContext
public class MyDataContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType.IsEnum)
{
property.SetValueConverter(new EnumToDbStringEquivalentConvertor<EmployeeType>());
}
}
}
}
}
Value Converter
public class EnumToDbStringEquivalentConvertor<T> : ValueConverter<T, string>
{
public EnumToDbStringEquivalentConvertor(ConverterMappingHints mappingHints = null) : base(convertToProviderExpression, convertFromProviderExpression, mappingHints)
{ }
private static Expression<Func<T, string>> convertToProviderExpression = x => ToDbString(x);
private static Expression<Func<string, T>> convertFromProviderExpression = x => ToEnum<T>(x);
public static string ToDbString<TEnum>(TEnum tEnum)
{
var enumType = tEnum.GetType();
var enumTypeMemberInfo = enumType.GetMember(tEnum.ToString());
EnumDbStringValueAttribute enumDbStringValueAttribute = (EnumDbStringValueAttribute)enumTypeMemberInfo[0]
.GetCustomAttributes(typeof(EnumDbStringValueAttribute), false)
.FirstOrDefault();
return enumDbStringValueAttribute.StringValue;
}
public static TEnum ToEnum<TEnum>(string stringValue)
{
// Code not included for brevity
}
}
This code (I'm glad to say) seems to be working without any issues.
My problem
The documentation around value converters seems to suggest the way we assign them in the OnModelCreating method is to physically assign each individual type converter to each individual property in the model. I don't want to have to do this - I want my model to be the driver. I'll implement this later but, for now, in the current version of the code I'm looping through the entity types in my model, checking the 'IsEnum' property value and then assigning the value converter at that point.
My problem is that the SetValueConverter extension method that I'm using requires me to pass it a new instance of EnumToDbStringEquivalentConvertor, which in my example is hard coded to be EnumToDbStringEquivalentConvertor which works. However I don't want that to be hardcoded - I want to pass the entity type's ClrType.
I have used reflection to create generic types and generic methods before but I can't seem to find the right code to get this working.
This:
public class MyDataContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType.IsEnum)
{
var converterType = typeof(EnumToDbStringEquivalentConvertor<>);
var genericConverterType = converterType.MakeGenericType(property.ClrType);
MethodInfo setValueConverterMethodInfo = typeof(MutablePropertyExtensions).GetMethod("SetValueConverter");
setValueConverterMethodInfo.Invoke(property,
new object[] { property, Activator.CreateInstance(genericConverterType) });
}
}
}
}
}
gives me an error of "System.MissingMethodException: 'No parameterless constructor defined for this object.'" on the GetModel method in Microsoft.EntityFrameworkCore.Infrastructure
So my question is can anyone advise me of how I can pass my value converter generically to EF Core's 'SetValueConveter' method?
Thank you in advance for your assistance.
You are almost there. The problem is this code
Activator.CreateInstance(genericConverterType)
which tries to find and invoke parameterless constructor of your converter class. But your class constructor does have a parameter, although optional. Optional parameters are just compiler sugar; when using reflection you should pass them explicitly.
So you need to use the CreateInstance overload accepting params object[] args and pass null for mappingHints.
Also, there is no need to call SetValueConverter via reflection - it's part of the public API.
The working code could be like this:
if (property.ClrType.IsEnum)
{
var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
.MakeGenericType(property.ClrType);
var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
property.SetValueConverter(converter);
}
We have a BaseClass and a set of derived POCO classes (see DerivedClassA). They map to an existing key-value store in the database that we cannot change at this point.
Each property on the derived class will map to a key in the store. The keys that the properties represent are string values that are either the property name or (if present) the custom attribute MyAttribute.Key as demonstrated on PropertyC below. The common use case for the attribute is if the key begins with an integer which is invalid for a C# property name (and we cannot change the keys).
public class BaseClass
{
public int BaseClass Id { get; set; } = 0;
}
public class DerivedClassA : BaseClass
{
public int PropertyA { get; set; }
public int PropertyB { get; set; }
[MyAttribute(Key = "404Property")]
public int PropertyC { get; set; }
}
In code, we need to get the key values as strings. After some wrestling and digging from other SO Answers (I do not claim any level of expertise with generics), we came up with the GetKey() method in the derived BaseClass<T> below. Note that GetCustomAttributeValue<T>() is a custom helper method that returns the value of an attribute (there may be a better way to do that, but that's out of scope for this question).
public class BaseClass<T> : BaseClass where T : BaseClass<T>
{
public string GetKey(Expression<Func<T, object>> property)
{
var memberInfo = GetMemberInfo(property);
return GetAttributeKey(memberInfo) ?? memberInfo?.Name;
}
private static MemberInfo GetMemberInfo(Expression<Func<T, object>> property) =>
(property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression)?.Member;
private static string GetAttributeKey(MemberInfo member) =>
member.GetCustomAttributeValue<string>(typeof(MyAttribute), "Key");
}
This solution seems to work if we derive the classes from the new BaseClass<T>
public class DerivedClassA : BaseClass<T> {
...
}
The GetKey() is now called as follows:
var derivedClassA = new DerivedClassA();
var propertyCKey = derivedClassA.GetKey(p => p.PropertyC);
We have a requirement that BaseClass needs to stay around, however, and we do not want the complexity of both the non-generic and the generic versions of BaseClass.
When I try to move GetKey() into the non-generic BaseClass, it no longer has the T type of the derived class which it needs to lookup the set of properties on the derived class. I do not want to add duplicate GetKey()s in each derived class.
Question:
Is there a way to move the GetKey() method (possibly re-writing it) into BaseClass rather than introducing the new BaseClass<T> only for supporting GetKey()?
Additional Background:
We are trying to wrap object-oriented/strong typing around a data store that is just a table that looks like:
| PreferenceId | UserId | PreferenceString |
Each derived class represents a different PreferenceId. Each PreferenceString is just a string of key/values "serialized" in a way custom to that PreferenceId (there is no rhyme/reason that can be shared across all PreferenceIds). This should all be redesigned at some point, but we are trying to wrap the current store in some kind of strong typing as a step in the transition.
As for me, all this structure in general seems to be crazy and overly complex.
Consider rewriting the whole approach instead of changing the GetKey method.
In general, GetKey in your base class breaks single-responsibility principle.
If I had to do this, I would just extract this functionality into a static class:
public static class CustomKeysHelper
{
public static string GetKey<T>(Expression<Func<T, object>> property) where T : BaseClass
{
var memberInfo = GetMemberInfo(property);
return GetAttributeKey(memberInfo) ?? memberInfo?.Name;
}
private static MemberInfo GetMemberInfo<T>(Expression<Func<T, object>> property) =>
(property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression)?.Member;
private static string GetAttributeKey<T>(MemberInfo member) =>
member.GetCustomAttributeValue<string>(typeof(MyAttribute), "Key");
}
// Usage:
string cKey = CustomKeysHelper.GetKey<DerivedClassA>(dca => dca.PropertyC);
Yes, it makes every GetKey call look longer, but it separates this logic and makes your intention clear.
Just in case you have instance of object BaseClass and want to extract property name from instance, but not type, then you can an extension method:
public static class CustomKeysHelper
{
// ... see above
public static string GetKey<T>(this T obj, Expression<Func<T, object>> property) where T : BaseClass
{
return GetKey<T>(property);
}
}
// Now, you can do this:
DerivedClassA derAInstance = ...;
derAInstance.GetKey(dca => dca.PropertyC);
I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).