I have following code which is working fine . but i need to use extension method with its Enum type.
public class Dropdown
{
public Dropdown() { }
public Dropdown(int id, string name)
{
Id = id;
Name = name;
}
public Dropdown(string name)
{
Name = name;
}
public int Id { get; set; }
public string StringId { get; set; }
public string Name { get; set; }
}
public enum AccidentTypeEnum
{
[Display(Name = "Minor")]
Minor = 0,
[Display(Name = "Major")]
Major = 1,
[Display(Name = "Severe")]
Severe = 2
}
Extension Method
public static class EnumExtensions
{
public static List<Dropdown> ConvertToDropdown(this Enum mEnum)
{
var dropDownlist = new List<Dropdown>();
var enumType = mEnum.GetType();
var enumValuies = Enum.GetValues(enumType);
foreach (var singleValue in enumValuies)
{
dropDownlist.Add(new Dropdown { Id = (int)singleValue, Name = singleValue.ToString() });
}
return dropDownlist;
}
}
Question:
Above code is working fine if i call extension method using below lines
var TestAcident = AccidentTypeEnum.Major;
var resultDropdown = TestAcident.ConvertToDropdown();
But how can i call it just like below lines
var resultDropdown = AccidentTypeEnum.ConvertToDropdown(); //<-- i need to use like this. but it not working
You can do that
List<AccidentTypeEnum> types = Utils<AccidentTypeEnum>.ConvertToDropdown();
this way:
/// <summary>Pseudo extension class for enumerations</summary>
/// <typeparam name="TEnum">Enumeration type</typeparam>
public class Utils<TEnum> where TEnum : struct, IConvertible
{
public static List<Dropdown> ConvertToDropdown()
{
var enumType = typeof(TEnum);
return enumType.IsEnum
? enumType.GetEnumValues()
.OfType<TEnum>()
.Select(e => new Dropdown
{
Id = Convert.ToInt32(Enum.Parse(enumType, e.ToString()) as Enum),
Name = GetDisplay(e)
})
.ToList()
: throw new ArgumentException($"{enumType.Name} is not enum");
}
private static string GetDisplay<T>(T value)
{
var enumValueText = value.ToString();
var displayAttribute = value
.GetType()
.GetField(enumValueText)
.GetCustomAttributes(typeof(DisplayAttribute), false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
return displayAttribute == null ? enumValueText : displayAttribute.Description;
}
}
Related
I am using dotConnect for MySQL product of Devart. MySQL database structure likes this:
I am getting data like this:
public int user_id { get; set; } = 2;
public string lang { get; set; } = "en"; // Depending on the situation, it may also be "tr".
private readonly mainDataContext _db = new();
var cats = _db.categories.Where(s => s.u_id == user_id);
foreach (var cat in cats)
{
MessageBox.Show(cat.name_en);
}
In the MessageBox.Show I can not use cat.name + "_" + lang like PHP. I don't know how to get over this problem.
In nutshell, you can use this:
cat.GetType().GetProperty("name_" + lang).GetValue(cat,null))
But it's better to call a method to get value:
static public T getval<T>(Object obj, string field)
{
return (T)obj.GetType().GetProperty(field).GetValue(obj, null);
}
Here is a full example:
using System;
namespace Example
{
public class user
{
public int user_id { get; set; } = 2;
public string name_en { get; set; }
public string name_tr { get; set; }
}
class Program
{
static public T getval<T>(Object obj, string field)
{
return (T)obj.GetType().GetProperty(field).GetValue(obj, null);
}
static void Main(string[] args)
{
List<user> u = new List<user>();
u.Add(new user { user_id = 1, name_en = "Foods", name_tr = "name_tr value 1" });
u.Add(new user { user_id = 2, name_en = "Pizza", name_tr = "name_tr value 2" });
u.Add(new user { user_id = 2, name_en = "Other", name_tr = "name_tr vale 3" });
var lang = "en";
var cats = u.Where(s => s.user_id == 2);
foreach (var cat in cats)
{
Console.WriteLine(getval<string>(cat,"name_"+lang));
}
return;
}
}
}
I have two DbSets:
public DbSet<Reports.Models.Application> Application { get; set; }
public DbSet<Reports.Models.Category> Category { get; set; }
In the controller, I'm creating two List<SelectListItem>s:
var applications = _context.Application
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
var categories = _context.Category
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
I'd like to refactor this into a single, private method:
private List<SelectListItem> SelectList<T>(bool blankListItem = false)
{
var selectListItems = _context.<T> <------ doesn't compile
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
if (blankListItem)
selectListItems.Insert(0, (new SelectListItem { Text = $"Choose {{T.ToString}}", Value = "" }));
return selectListItems;
}
And call it twice:
var applications = SelectList<Application>();
var categories = SelectList<Category>();
or
var applications = SelectList<Application>(true); // add "choose"
var categories = SelectList<Category>(true); // add "choose"
What's the right way to define the _context.<T> part? Perhaps this should be an extension method of the DbSet instead?
Maybe you can have your dbsets inherit a base class. which would be representing the generic type T.
Something like;
public class BaseClassForDbSets
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Application : BaseClassForDbSets
{
}
public class Category : BaseClassForDbSets
{
}
and then your private method;
private IEnumerable<SelectListItem> GetSelectList<T>(IEnumerable<T> dataSource, bool blankListItem = false) where T : BaseClassForDbSets
{
var selectListItems = dataSource
.Select(listItem => new SelectListItem
{
Value = listItem.Id.ToString(),
Text = listItem.Name
}
).ToList();
if (blankListItem)
selectListItems.Insert(0, (new SelectListItem { Text = $"Choose {nameof(T)}", Value = "" }));
return selectListItems;
}
Then you would call it like;
var applicationCollection = GetSelectList(_context.Application);
var categoryCollection = GetSelectList(_context.Category);
Do note - not tested
My solution uses a different approach, but same result.
Start with an interface:
public interface IBaseSelectItem
{
int Id { get; set; }
string Name { get; set; }
}
Have your entities (Application and Category) implement the interface:
public partial class Category : IBaseSelectItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Create an extension on DbSet:
public static IList<SelectListItem> AsSelectList<T>(this DbSet<T> dbSet, bool useChooseValueOption) where T : class, IBaseSelectItem
{
var selectList = dbSet
.Select(c => new SelectListItem { Value = c.Id.ToString(), Text = c.Name })
.ToList();
if (useChooseValueOption)
selectList.Insert(0, new SelectListItem { Value = "0", Text = "-Choose Value-" });
return selectList;
}
Then use like this:
var categoriesSelectList = _dbContext.Categories.AsSelectList();
I am new with C# and I have some troubles with enum.
I have Enum defined like this:
public enum CustomFields
{
[Display(Name = "first_name")]
FirstName = 1,
[Display(Name = "last_name")]
LastName = 2,
}
What I need is code which will check does display name exist and if so return enum value.
So if I have display name:
var name = "first_name";
I need something like:
var name = "first_name";
CustomFields.getEnumValue(name);
This should return:
CustomFields.FirstName;
You could use generics:
public class Program
{
private static void Main(string[] args)
{
var name = "first_name";
CustomFields customFields = name.GetValueFromName<CustomFields>();
}
}
public enum CustomFields
{
[Display(Name = "first_name")]
FirstName = 1,
[Display(Name = "last_name")]
LastName = 2,
}
public static T GetValueFromName<T>(this string name) where T : Enum
{
var type = typeof(T);
foreach (var field in type.GetFields())
{
if (Attribute.GetCustomAttribute(field, typeof(DisplayAttribute)) is DisplayAttribute attribute)
{
if (attribute.Name == name)
{
return (T)field.GetValue(null);
}
}
if (field.Name == name)
{
return (T)field.GetValue(null);
}
}
throw new ArgumentOutOfRangeException(nameof(name));
}
Try the following.
void Main()
{
CustomFields value1 = GetEnumValue("first_name");
CustomFields value2 = GetEnumValue("last_name");
}
static Dictionary<string, CustomFields> displayNameMapping;
static CustomFields GetEnumValue(String displayName){
if (displayNameMapping == null){
var enumType = typeof(CustomFields);
var displayAttributeType = typeof(DisplayAttribute);
CustomFields? found = null;
displayNameMapping = new Dictionary<string, CustomFields>();
Enum.GetNames(enumType).ToList().ForEach(name=>{
var member = enumType.GetMember(name).First();
var displayAttrib = (DisplayAttribute)member.GetCustomAttributes(displayAttributeType, false).First();
displayNameMapping.Add(displayAttrib.Name, (CustomFields)Enum.Parse(enumType, name));
});
}
return displayNameMapping[displayName];
}
// Define other methods and classes here
public enum CustomFields
{
[Display(Name = "first_name")]
FirstName = 1,
[Display(Name = "last_name")]
LastName = 2,
}
Does anyone know how to access the "Display Name" data annotation of enum types?
I have an enum type with display names
class enum SomethingType {
[Display(Name = "Type 1")]
Type1,
[Display(Name = "Type 2")]
Type2
}
and a model class that references to it
class ModelClass {
public SomethingType Type {get; set;}
}
How do I display the display name for the values in ModelClass?
Thanks.
I think you are looking for something like this:
class ModelClass
{
public SomethingType MyType {get; set;}
public string TypeName {
get
{
var enumType = typeof(SomethingType);
var field = enumType.GetFields()
.First(x => x.Name == Enum.GetName(enumType, MyType));
var attribute = field.GetCustomAttribute<Display>();
return attribute.Name;
}
}
You could use reflection to access properties of the attribute:
Type = SomethingType.Type2;
var memberInfo = Type.GetType().GetMember(Type.ToString());
if (memberInfo.Any())
{
var attributes = memberInfo.First().GetCustomAttributes(typeof(DisplayAttribute), false);
if (attributes.Any())
{
var name = ((DisplayAttribute)attributes.First()).Name; // Type 2
}
}
You can create a generic helper method that will read data from these attributes.
public static string GetAttributeValue<T>(this Enum e, Func<T, object> selector) where T : Attribute
{
var output = e.ToString();
var member = e.GetType().GetMember(output).First();
var attributes = member.GetCustomAttributes(typeof(T), false);
if (attributes.Length > 0)
{
var firstAttr = (T)attributes[0];
var str = selector(firstAttr).ToString();
output = string.IsNullOrWhiteSpace(str) ? output : str;
}
return output;
}
Example:
var x = SomethingType.Type1.GetAttributeValue<DisplayAttribute>(e => e.Name);
.......
class ModelClass
{
public SomethingType Type { get; set; }
public string TypeName
{
get { return Type.GetAttributeValue<DisplayAttribute>(attribute => attribute.Name); }
}
}
I have a class, which is created and populated from an xml string, I've simplified it for example purposes:
[XmlRoot("Person")]
public sealed class Person
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Location")]
public string Location { get; set; }
[XmlElement("Emails", Type = typeof(PersonEmails)]
public PersonEmails Emails { get; set; }
}
public class PersonEmails
{
[XmlElement("Email", Type = typeof(PersonEmail))]
public PersonEmail[] Emails { get; set; }
}
public class PersonEmail
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
To extract the information, I'm trying to load them into another class, which is simply:
public class TransferObject
{
public string Name { get; set; }
public ObjectField[] Fields { get; set; }
}
public class ObjectField
{
public string Name { get; set; }
public string Value { get; set; }
}
I'm only populating "Fields" from the other object, which would simply be (Name = "Location", Value = "London"), but for Emails, (Name = "Email"+Type, Value = jeff#here.com)
Currently I can populate all the other fields, but I'm stuck with Emails, and knowing how to dig deep enough to be able to use reflection (or not) to get the information I need. Currently I'm using:
Person person = Person.FromXmlString(xmlString);
List<ObjectField> fields = new List<ObjectField>();
foreach (PropertyInfo pinfo in person.getType().GetProperties()
{
fields.Add(new ObjectField { Name = pinfo.Name, Value = pinfo.getValue(person, null).ToString();
}
How can I expand on the above to add all my emails to the list?
You are trying to type cast a complex values type to string value so you lost the data. Instead use following code:
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "Person One";
person.Location = "India";
person.Emails = new PersonEmails();
person.Phones = new PersonPhones();
person.Emails.Emails = new PersonEmail[] { new PersonEmail() { Type = "Official", Value = "xyz#official.com" }, new PersonEmail() { Type = "Personal", Value = "xyz#personal.com" } };
person.Phones.Phones = new PersonPhone[] { new PersonPhone() { Type = "Official", Value = "789-456-1230" }, new PersonPhone() { Type = "Personal", Value = "123-456-7890" } };
List<ObjectField> fields = new List<ObjectField>();
fields = GetPropertyValues(person);
}
static List<ObjectField> GetPropertyValues(object obj)
{
List<ObjectField> propList = new List<ObjectField>();
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var value = pinfo.GetValue(obj, null);
if (pinfo.PropertyType.IsArray)
{
var arr = value as object[];
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].GetType().IsPrimitive)
{
propList.Add(new ObjectField() { Name = pinfo.Name + i.ToString(), Value = arr[i].ToString() });
}
else
{
var lst = GetPropertyValues(arr[i]);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
else
{
if (pinfo.PropertyType.IsPrimitive || value.GetType() == typeof(string))
{
propList.Add(new ObjectField() { Name = pinfo.Name, Value = value.ToString() });
}
else
{
var lst = GetPropertyValues(value);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
return propList;
}
}
Check this snippet out:
if(pinfo.PropertyType.IsArray)
{
// Grab the actual instance of the array.
// We'll have to use it in a few spots.
var array = pinfo.GetValue(personObject);
// Get the length of the array and build an indexArray.
int length = (int)pinfo.PropertyType.GetProperty("Length").GetValue(array);
// Get the "GetValue" method so we can extact the array values
var getValue = findGetValue(pinfo.PropertyType);
// Cycle through each index and use our "getValue" to fetch the value from the array.
for(int i=0; i<length; i++)
fields.Add(new ObjectField { Name = pinfo.Name, Value = getValue.Invoke(array, new object[]{i}).ToString();
}
// Looks for the "GetValue(int index)" MethodInfo.
private static System.Reflection.MethodInfo findGetValue(Type t)
{
return (from mi in t.GetMethods()
where mi.Name == "GetValue"
let parms = mi.GetParameters()
where parms.Length == 1
from p in parms
where p.ParameterType == typeof(int)
select mi).First();
}
You can definately do it with Reflection... You can take advantage of the fact that a Type can tell you if it's an array or not (IsArray)... and then take advantage of the fact that an Array has a method GetValue(int index) that will give you a value back.
Per your comment
Because Emails is a property within a different class, recursion should be used. However the trick is knowing when to go to the next level. Really that is up to you, but
if it were me, I would use some sort of Attribute:
static void fetchProperties(Object instance, List<ObjectField> fields)
{
foreach(var pinfo in instance.GetType().GetProperties())
{
if(pinfo.PropertyType.IsArray)
{
... // Code described above
}
else if(pinfo.PropertyType.GetCustomAttributes(typeof(SomeAttribute), false).Any())
// Go the next level
fetchProperties(pinfo.GetValue(instance), fields);
else
{
... // Do normal code
}
}
}