I want the value to be initialized according to the attribute When Instance is created.
class FieldAttr : Attribute
{
public readonly string key;
public Key_FieldAttr(string key)
{
this.key = key;
}
}
class CtorAttr : Attribute
{
}
static string FromTable(string key)
{
// will return localized string of key from table.
}
...
class LocalizedMetadata
{
[FieldAttr("NAME")]
public string Name { get; }
[FieldAttr("DESC")]
public readonly string Description;
[CtorAttr]
public LocalizedMetadata(string header)
{
// I want "Key" attribute to do as below...
// this.Name = FromTable(header + "NAME");
// this.Description = FromTable(header + "DESC");
}
}
public static void Main(string[] args)
{
var foo = new LocalizedMetadata("UNITY_");
Console.WriteLine(foo.Name); // = FromTable("UNITY_NAME");
Console.WriteLine(foo.Description); // = FromTable("UNITY_DESC");
}
I don't know where to attach what kind of Attribute (Field, Construction, Class) will be possible.
I looked up documents about Attribute and Reflection, but I don't know if this is possible.
I think below solution will solve your problem.
class LocalizedMetadata
{
[FieldAttr("NAME")]
public string Name { get; set; }
[FieldAttr("DESC")]
public string Description { get; set; }
public LocalizedMetadata(string header)
{
foreach (var property in typeof(LocalizedMetadata).GetProperties())
{
foreach (var attr in property.GetCustomAttributes(false))
{
FieldAttr fieldAttr = (FieldAttr)attr;
property.SetValue(this, header + fieldAttr.Key);
}
}
}
}
public static void Main(string[] args)
{
var foo = new LocalizedMetadata("UNITY_");
Console.WriteLine(foo.Name);
Console.WriteLine(foo.Description);
}
Output:
UNITY_NAME
UNITY_DESC
Related
i am working on source generator and i need to read properties data type as string
Is there any solution to get data type name such as int, string, bool, etc...?
EDIT:
var typeName = property.Type.ToString(); throws exception
EDIT 2:
public void Execute(GeneratorExecutionContext context)
{
var model = new TemplateModel();
var syntaxReceiver = (SolnetParsableClassExplorer)context.SyntaxReceiver;
syntaxReceiver?.SolnetDtos.ForEach(dto =>
{
var typeNodeSymbol = context.Compilation
.GetSemanticModel(dto.SyntaxTree)
.GetDeclaredSymbol(dto);
var dataClass = new DataClassModel();
foreach (var member in dto.DescendantNodes())
{
if (member is PropertyDeclarationSyntax property)
{
dataClass.AddProperty(property);
}
}
dataClass.ClassName = dto.Identifier.ValueText;
model.DataClassModels.Add(dataClass);
});
...
}
internal class SolnetParsableClassExplorer : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> SolnetDtos { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classSyntax &&
classSyntax.HaveAttribute("SolnetDto"))
{
SolnetDtos.Add(classSyntax);
}
}
}
internal class DataClassModel
{
public List<DataField> Properties = new List<DataField>();
public string Namespace { get; set; }
public string ClassName { get; set; }
public int Size => Properties.Sum(p => p.Size);
public void AddProperty(PropertyDeclarationSyntax property)
{
// Here generator throw exception
var typeName = property.Type.ToString();
Properties.Add(
new DataField
{
PropertyName = property.Identifier.ValueText,
DataType = typeName,
Size = GetPropertySize(compilation, property, typeName),
WriteMethod = GetWriteMethod(typeName),
ReadMethod = GetReadMethod(typeName),
Offset = Size
});
}
I hope I understood your question correctly. To retrieve the string representation, you could use the TypeSyntax.ToString()
var strType = propertyDeclarationSyntax.Type.ToString();
For example, for the following
public string Bar1 { get; set; }
public int Bar2 { get; set; }
public long Bar3 { get; set; }
Result
Property Bar1 : string
Property Bar2 : int
Property Bar3 : long
I have a class that I've created
public class DataRecord
{
public string PayerAccount { get; set; }
public string GlobalEntityType { get; set; }
public string GlobalEntityNumber { get; set; }
}
I am now trying to access this DataRecord in a different method through the use of a variable
public List<DataTest> CountAndFrequency(IEnumerable records, string ColumnName, int numResults)
{
foreach (DataRecord record in records)
{
record.ColumnName = record.ColumnName.ToUpper();
}
}
I am getting the error that DataRecord does not contain a definition for ColumnName, which of course makes sense. The question is, how do I combat this issue? I've been scouring the internet to no avail and would appreciate any help.
Thanks in advance!
You can use reflection for this. Try this
public static List<DataTest> CountAndFrequency(IEnumerable<DataRecord> records, string ColumnName, int numResults)
{
foreach (DataRecord record in records)
{
var prop = typeof(DataRecord).GetProperty(ColumnName);
var value = prop.GetValue(record).ToString().ToUpper();
prop.SetValue(record, value);
}
}
If you want to access data via a string name, you store the data in a Dictionary<string,string>.
public class DataRecord
{
private readonly Dictionary<string, string> data;
public DataRecord()
{
this.data = new Dictionary<string, string>();
}
// Access data with names
public string this[string columnName]
{
get{ return data[columnName]; }
set{ data[columnName] = value;}
}
// Fake properties
public string PayerAccount
{
get => data[nameof(PayerAccount)];
set => data[nameof(PayerAccount)] = value;
}
public string GlobalEntityType
{
get => data[nameof(GlobalEntityType)];
set => data[nameof(GlobalEntityType)] = value;
}
public string GlobalEntityNumber
{
get => data[nameof(GlobalEntityNumber)];
set => data[nameof(GlobalEntityNumber)] = value;
}
}
class Program
{
static void Main(string[] args)
{
var record = new DataRecord
{
PayerAccount = "10024",
GlobalEntityType = "QXT",
GlobalEntityNumber = "509382"
};
var number = record["GlobalEntityNumber"];
// 509382
}
}
I have a multilingual database, which returns values based on a key and an enum Language. When I convert a DB object to a model, I want the model to contain the translated value based on the key and the current language.
The key comes from the DB object but how can I pass the current language to the the Mapper.Map() function?
Currently, I am using a [ThreadStatic] attribute to set the culture before calling Mapper.Map<>, and to retrieve it in the TypeConverter.
public enum Language
{
English, French, Italian, Maltese
}
public class MultilingualValue<T>
{
public Dictionary<Language, T> Value { get; set; }
public MultilingualValue()
{
this.Value = new Dictionary<Language, T>();
}
}
public class PersonData
{
public string FirstName { get; set; }
public MultilingualValue<string> City { get; set; }
}
public void MapPerson()
{
PersonData personData = new PersonData();
personData.FirstName = "John";
personData.City = new MultilingualValue<string>();
personData.City.Value[ Language.English] = "The Zurrieq";
personData.City.Value[Language.French] = "Le Zurrieque";
MultilingualValueData.CurrentLanguage = Language.English;
var personModel = Mapper.Map<PersonData, PersonModel>(personData);
}
public class MultilingualValueToBasicDataTypeConverter<T> : ITypeConverter<MultilingualValue<T>, T>
{
public T Convert(ResolutionContext context)
{
var currentLanguage = MultilingualValueData.CurrentLanguage; //THIS IS THE [ThreadStatic] VARIABLE
if (currentLanguage == null) throw new InvalidOperationException("Please make sure to fill in CurrentLanguage");
MultilingualValue<T> sourceMultilingualValue = (MultilingualValue < T > )context.SourceValue;
T destinationValue = default(T);
if (sourceMultilingualValue != null)
{
destinationValue = sourceMultilingualValue.Value[currentLanguage.Value];
}
return destinationValue;
}
}
public static class MultilingualValueData
{
[ThreadStatic]
public static Language? CurrentLanguage;
}
I left out the configurations as I think they're unneccessary for this example. If you need them, I'll post them as well.
While this works, I find this workaround quite ugly. Is there any way to pass data through the ResolutionContext?
Just use the Map overload that takes a Action<IMappingOperationOptions>. You can add configuration elements to the Items property that are then passed to your ITypeConverter
public class CustomConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
return "translated in " + context.Options.Items["language"];
}
}
internal class Program
{
private static void Main(string[] args)
{
AutoMapper.Mapper.CreateMap<string, string>().ConvertUsing<CustomConverter>();
var result = AutoMapper.Mapper.Map<string, string>("value" , opt => opt.Items["language"] = "english");
Console.Write(result); // prints "translated in english"
Console.ReadLine();
}
}
I have a DTO that looks like this:
public class MyDto
{
[MyAttribute("attribute1")]
public string Property1 {get;set;}
[MyAttribute("attribute2")]
public string Property2 {get;set;}
}
If I have the string "attribute1", how do I use that to get to the value of Property1 in an instance of MyDto?
Use Reflection. Unfortunately, there's no way to obtain the property from an attribute: you have to iterate through each property and check its attribute.
Not the most robust code, but try this:
public class MyAttributeAttribute : Attribute
{
public MyAttributeAttribute(string value)
{
Value=value;
}
public string Value { get; private set; }
}
public class MyDto
{
[MyAttribute("attribute1")]
public string Property1 { get; set; }
[MyAttribute("attribute2")]
public string Property2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
MyDto dto=new MyDto() { Property1="Value1", Property2="Value2" };
string value=GetValueOf<string>(dto, "attribute1");
// value = "Value1"
}
public static T GetValueOf<T>(MyDto dto, string description)
{
if(string.IsNullOrEmpty(description))
{
throw new InvalidOperationException();
}
var props=typeof(MyDto).GetProperties().ToArray();
foreach(var prop in props)
{
var atts=prop.GetCustomAttributes(false);
foreach(var att in atts)
{
if(att is MyAttributeAttribute)
{
string value=(att as MyAttributeAttribute).Value;
if(description.Equals(value))
{
return (T)prop.GetValue(dto, null);
}
}
}
}
return default(T);
}
}
I have created an Attribute, call MyAttribute, which is performing some security and for some reason the Constructor is not being fired, any reason why?
public class Driver
{
// Entry point of the program
public static void Main(string[] Args)
{
Console.WriteLine(SayHello1("Hello to Me 1"));
Console.WriteLine(SayHello2("Hello to Me 2"));
Console.ReadLine();
}
[MyAttribute("hello")]
public static string SayHello1(string str)
{
return str;
}
[MyAttribute("Wrong Key, should fail")]
public static string SayHello2(string str)
{
return str;
}
}
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
public MyAttribute(string VRegKey)
{
if (VRegKey == "hello")
{
Console.WriteLine("Aha! You're Registered");
}
else
{
throw new Exception("Oho! You're not Registered");
};
}
}
Attributes are applied at compile time, and the constructors used only to fill in the properties. Attributes are metadata, and can only be examined at runtime.
In fact, Attributes should not contain any behavior at all.
Actually it fails, but only if you are trying to get attribute properties. Here is an example that fails:
using System;
public class Driver
{
// Entry point of the program
public static void Main(string[] Args)
{
Console.WriteLine(SayHello1("Hello to Me 1"));
Console.WriteLine(SayHello2("Hello to Me 2"));
Func<string, string> action1 = SayHello1;
Func<string, string> action2 = SayHello2;
MyAttribute myAttribute1 = (MyAttribute)Attribute.GetCustomAttribute(action1.Method, typeof(MyAttribute));
MyAttribute myAttribute2 = (MyAttribute)Attribute.GetCustomAttribute(action2.Method, typeof(MyAttribute));
Console.ReadLine();
}
[MyAttribute("hello")]
public static string SayHello1(string str)
{
return str;
}
[MyAttribute("Wrong Key, should fail")]
public static string SayHello2(string str)
{
return str;
}
}
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
public string MyProperty
{
get; set;
}
public string MyProperty2
{
get;
set;
}
public MyAttribute(string VRegKey)
{
MyProperty = VRegKey;
if (VRegKey == "hello")
{
Console.WriteLine("Aha! You're Registered");
}
else
{
throw new Exception("Oho! You're not Registered");
};
MyProperty2 = VRegKey;
}
}