Localize enum property in asp.net mvc - c#

I have enum property
public enum MyType
{
House = 0,
Apartment = 1
}
now I want to localize this enum using localized resx strings, so I add
public enum MyType
{
[LocalizedName("House", typeof(LocalizedUIStrings))]
House = 0,
[LocalizedName("Apartment ", typeof(LocalizedUIStrings))]
Apartment = 1
}
and localizedname helper class
public class LocalizedNameAttribute : Attribute
{
private readonly Type _resourceType;
private readonly string _resourceKey;
public LocalizedNameAttribute(string resourceKey, Type resourceType)
{
_resourceType = resourceType;
_resourceKey = resourceKey;
DisplayName = (string)_resourceType
.GetProperty(_resourceKey, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
.GetValue(null, null);
}
public string DisplayName { get; private set; }
}
in the view I'm trying to use this localized property like this
#Html.DisplayFor(modelItem => item.MyType)
It still returns non localized string, no errors (either on compile or runtime).
What I'm doing wrong here?

Related

How to implement reflection to get an object field based on a generic enum

I think it is a rather simple issue, but I can't really get my head around reflection things.
I'm looking for a way to initialize DropDownList based on enums with default value in Razor page
I have the following CustomModel class which have many enum properties:
public class CustomModel {
public EnumA A { get; set; }
public EnumB B { get; set; }
// Other properties based on EnumC, EnumD, etc.
}
And the view model where I want to populate each enum:
public class CustomViewModel
{
public CustomModel Custom { get; set; }
public SelectList As { get; set; }
public SelectList Bs { get; set; }
// Other SelectList properties for all the different enums
public CustomViewModel(CustomModel custom) // Will need to think about some dependency injection
{
Custom = custom;
As = InitializeDropDownList<A>();
Bs = InitializeDropDownList<B>();
// Idem for all other enums
}
// Probably move it to an extension method
private SelectList InitializeDropdownList<T>() where T : struct, Enum
{
// string value = ...
new SelectList(new Descriptions<T>(), value)
}
}
// Returns the string values corresponding to the generic enum
public class Descriptions<T> : List<string> where T : struct, Enum
{
public Descriptions()
{
// Enumerates the T values
foreach (T e in Enum.GetValues(typeof(T)))
{
// A bit simplified, because I actually use an other Description extension method
Add(e.ToString());
}
}
}
Is there a way to get the value in the InitializeDropdownList function using generics and reflection as follows :
T = EnumA => value = CustomModel.A
T = EnumB => value = CustomModel.B
...
I think I should use FieldInfo[] fields = Custom.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); and compare it to the actual name of T and then get the value of the field whih I don't know how to do. Does anyone have an idea of how to achieve it?
Thanks for any insights!

Is it possible to create a type with two properties having the same name?

According to the documentation, Type.GetProperty(string, BindingFlags) throws AmbiguousMatchException when:
More than one property is found with the specified name and matching
the specified binding constraints
I'm looking for an example type where the GetProperty method would throw because more than one property is found with the same name. I created an inheritance relationship (A : B) where both classes define the same named public property (using the new keyword), with BindingFlags = Public | Instance, but that doesn't throw.
In C#, a class can implement multiple indexers, all of which are called Item.
public class Class1
{
public string this[int firstParameter]
{
get { return ""; }
}
public string this[string firstParameter, int secondParameter]
{
get { return ""; }
}
}
Then you can produce the exception using this:
class Program
{
static void Main()
{
// This will throw AmbiguousMatchException:
typeof(Class1).GetProperty("Item", BindingFlags.Public | BindingFlags.Instance);
}
}
That will produce the AmbiguousMatchException with a single class and the Public and Instance binding flags.
This is an example using BindingFlags.FlattenHierarchy that leads to a name clash between a static and instance property.
public class Program
{
public class A
{
public static string Property { get; set; }
}
public class B : A
{
public string Property { get; set; }
}
public static void Main(string[] args)
{
var type = typeof(B);
var property = type.GetProperty(
"Property",
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy);
}
}
You can get ambiguous match if you'll play with BindingFlags. For example BindingFlags.IgnoreCase allows you to get this exception despite not having properties with the same name:
class MyClass
{
public string MyProperty {get; set;}
public int Myproperty {get; set;}
}
typeof(MyClass).GetProperty("MyProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase)
Also next set up with BindingFlags.FlattenHierarchy produces the mentioned error:
class MyClass : Base
{
public new string MyProperty { get; set; }
}
class Base
{
public static string MyProperty {get;set;}
}
typeof(MyClass).GetProperty("MyProperty",
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);

How to use Reflection to read Static Properties from a Nested Class?

I have some configuration stored in the following data structure which uses nested classes for Organization.
public abstract class LogoSpec
{
public abstract byte[] Logo { get; set; }
public class Web
{
public static float Height { get; set; }
public class A4 : Web
{
public static float Left { get; set; }
}
}
}
public class SampleLogo : LogoSpec
{
public override byte[] Logo { get; set; }
}
I can easily use it when I exactly know what value to use at design time
// Setting values
SampleLogo.Web.A4.Height = 10.25f;
How can I write a function that retrieves this value at runtime?
float GetValue(string logoName = "SampleLogo", string layout = "Web", string paperSize = "A4", string property = "Height");
The way to get that property is actually very straight forward, but you have to provide all necessary BindingFlags:
PropertyInfo p = typeof(SampleLogo.Web.A4).GetProperty("Height",
BindingFlags.Static |
BindingFlags.FlattenHierarchy |
BindingFlags.Public);
FlattenHierarchy is needed to also get properties of base classes.
Now you can use this PropertyInfo to get and set the value:
p.SetValue(null, 14f);
float height = (float)p.GetValue(null);
Update: The complete method could look like this:
public float GetValue(string logoName = "LogoSpec", string layout = "Web", string paperSize = "A4", string property = "Height")
{
Type logoType = Type.GetType(logoName);
Type layoutType = logoType?.GetNestedType(layout);
Type paperType = layoutType?.GetNestedType(paperSize);
PropertyInfo pi = paperType?.GetProperty("Height", BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public);
return (float?)pi?.GetValue(null) ?? 0f;
}
But note that instead for "LogoSpec" you would need to use an AssemblyQualifiedName or at least qualify the type name with its namespace.

Get object reference from PropertyInfo

I have a little problem, below my code:
public class A
{
public string ObjectA { get; set; }
}
public void Run()
{
A a = new A();
a.ObjectA = "Test number 1";
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo myPropertyInfo = a.GetType().GetProperty("ObjectA", flags);
object myNewObject = myPropertyInfo.GetValue(a);// Here should be reference
a.ObjectA = "Test number 2";//After this line value myNewObject continued "Test number 1"
}
So my value myNewObject must be in output "Test number 2". Is there any way? It is this at all possible?
Wrong!
You're getting the string rather than the instance of A using reflection.
Changing A.ObjectA doesn't change the string reference. Actually, you're setting a different string to the backing string class field by the ObjectA property...
Auto-properties are syntactic sugar to avoid explicitly declaring class fields to properties which perform nothing when getting or setting their values:
// For example:
public string Text { get; set; }
// is...
private string _text;
public string Text { get { return _text; } set { _text = value; } }
Now turn your code into regular one (no reflection):
A a = new A();
a.ObjectA = "hello world";
object myNewObject = a.ObjectA;
// Do you think now that myNewObject should change...? :)
a.ObjectA = "goodbye";
Is there any way? It is this at all possible?
No.
Maybe you can simulate this behavior using a Func<T>...
Func<object> myNewObjectGetter = () => myPropertyInfo.GetValue(a);
Now, whenever you call myNewObjectGetter() you're going to get the most fresh value of the whole property. BTW, this still doesn't address the impossible!
Is there any way?
No. You can't put a reference to a property into an object variable. Such a variable can only hold a normal value, such as the string you put into it.
That answers the question as asked. You can clarify what you want to achieve and maybe we can suggest a better way.
Probably there is no solution but I show some code
public class MyRows
{
public string Key { get; set; }
public int Id { get; set; }
public object Val { get; set; }
}
public abstract class BasicDTO
{
public int? Id { get; private set; }
public PropertyInfo[] PropertyDTO;
protected Type myType;
public BasicDTO()
{
Load();
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyDTO = myType.GetProperties(flags);
}
}
public class CustomerDTO : BasicDTO
{
public string Surname { get; set; }
public string Phone { get; set; }
public CustomerDTO() { }
protected override void Load()
{
myType = typeof(CustomerDTO);
}
}
Now my basic method
public void Run(BasicDTO dto)
{
PropertyInfo pi = dto.PropertyDTO.Where(x => x.Name == "Surname").SingleOrDefault();
MyRows mr = new MyRows();
mr.Val = pi.GetValue(dto);//Here I need reference
}
When I change CustomerDTO.Surname my value mr.Val it must also be changed.
As I wrote above, it is probably impossible, but maybe anybody have a idea.
BTW: Value mr.Val I use only for binding (WPF). So maybe you have any other suggestions, how solve problem. I will be grateful for your help

DropDownList and EditorTemplates

I have an entity called Invoice that I am extending for data annotations
[MetadataType(typeof(InvoiceMetadata))]
public partial class Invoice
{
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class InvoiceMetadata
{
// Name the field the same as EF named the property - "FirstName" for example.
// Also, the type needs to match. Basically just redeclare it.
// Note that this is a field. I think it can be a property too, but fields definitely should work.
[HiddenInput]
public int ID { get; set; }
[Required]
[UIHint("InvoiceType")]
[Display(Name = "Invoice Type")]
public string Status { get; set; }
[DisplayFormat(NullDisplayText = "(null value)")]
public Supplier Supplier { get; set; }
}
The Uhint[InvoiceType] causes the InvoiceType Editor Template to be loaded for this element.This templates is defined as
#model System.String
#{
IDictionary<string, string> myDictionary = new Dictionary<string, string> {
{ "N", "New" }
, { "A", "Approved" },
{"H","On Hold"}
};
SelectList projecttypes= new SelectList(myDictionary,"Key","Value");
#Html.DropDownListFor(model=>model,projecttypes)
}
i have many such hardcoded status lists in my program.I say hardcoded because they are not fetched from the database. Is there any other way to create templates for drop downs? How do I declare an enum in the model and have the drop down load the enum without having to pass it through a view model ?
Rather than "hard coding" your statuses I would either create an Enum or a Type Safe Enum. For your example I would use the latter.
For each of your required "status lists" create a separate class with your desired settings:
public sealed class Status
{
private readonly string _name;
private readonly string _value;
public static readonly Status New = new Status("N", "New");
public static readonly Status Approved = new Status("A", "Approved");
public static readonly Status OnHold = new Status("H", "On Hold");
private Status(string value, string name)
{
_name = name;
_value = value;
}
public string GetValue()
{
return _value;
}
public override string ToString()
{
return _name;
}
}
Utilizing reflection you can now get the fields of this class to create your required drop down lists. It would be beneficial to your project to either create an extension method or a helper class:
var type = typeof(Status);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
Dictionary<string, string> dictionary = fields.ToDictionary(
kvp => ((Status)kvp.GetValue(kvp)).GetValue(),
kvp => kvp.GetValue(kvp).ToString()
);
You can now create your select list much like you are currently doing:
var list = new SelectList(dictionary,"Key","Value");
Which will create a drop down list with the following html:
<select>
<option value="N">New</option>
<option value="A">Approved</option>
<option value="H">On Hold</option>
</select>

Categories

Resources