I used to get one name for one property, but now I get data with two names for one property. That is, it was ServiceName ->ServiceName, and it became ServiceName ->ServiceName, Name.
value assignment code:
PropertyInfo property = item.GetType().GetProperty(column.Caption);
var range = worksheet.Cell(i, listIndex[indexForList]);
if (range == null)
{
continue;
}
var value = ChangeType((object)range.Value, property.PropertyType);
property.SetValue(item, value);
I have a model:
public class ImportAdditionalService
{
public string ServiceName { get; set; }
public decimal? Price { get; set; }
}
Are there any attributes in this case for class properties, so that I can use reflection for two names?
Related
I'm storing data in a Firestore DB using .net. I'm using FirestoreData and FirestoreProperty attributes to control how objects are serialized to the DB. C# propeties, by default, are in PascalCase and I'd like them to be serialized in camelCase. I know I can set the name a property will be serialized in the FirestoreProperty attribute, but it's a really tedious and error proner task. Is there any way to configure Firestore .net client to by default serialize properties in camelCase?
Thanks
The FirestorePropertyAttribute defines two constructors. One allows to add a name by providing the parameter name:
The name to use within the Firestore document.
So you can simply set it for a property like
[FireStoreProperty("anyCase")]
public string AnyCase{ get; set; }
Doing this a silent way is not possible without modifying the underlying type. A possible approach is to implement a reflection based Document converter, changing the property names at runtime. You only need to define the converter once for each data class. Here is a possible approach:
//Sample data class
[FirestoreData(ConverterType = typeof(CamelCaseConverter<CustomCity>))]
public class CustomCity
{
public string Name { get; set; }
public string Country { get; set; }
public long Population { get; set; }
}
//Sample data class
[FirestoreData(ConverterType = typeof(CamelCaseConverter<CustomPerson>))]
public class CustomPerson
{
public string Name { get; set; }
public uint Age { get; set; }
}
//Conversion of camelCase and PascalCase
public class CamelCaseConverter<T> : IFirestoreConverter<T> where T : new()
{
public object ToFirestore(T value)
{
dynamic camelCased = new ExpandoObject();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string camelCaseName =
char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
((IDictionary<string, object>)camelCased)[camelCaseName] = property.GetValue(value);
}
return camelCased;
}
public T FromFirestore(object value)
{
if (value is IDictionary<string, object> map)
{
T pascalCased = new T();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string camelCaseName =
char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
property.SetValue(pascalCased, map[camelCaseName]);
}
return pascalCased;
}
throw new ArgumentException($"Unexpected data: {value.GetType()}");
}
My code below:
foreach (var PI in ObjType.GetProperties())
{
var metaData = ModelMetadataProviders.Current.GetMetadataForType(null, PI.GetType());
string DispName = metaData.DisplayName
}
ObjType is the type of an EF6 schema first entity with DisplayName been added as a Metadata class. The error above is probably because PI.GetType() returns the type of the PropertyInfo. But I really can't figure out how to get the property itself.
I have look into various example using:
ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
However, in my case, I am not using any Lambda Expression. I just need to construct a list of the properties' DisplayName and pass it on.
But I really can't figure out how to get the property itself.
You want PropertyInfo.PropertyType, so change PI.GetType() to PI.PropertyType.
I don't know if this will help you, but this is how i got the MetaDataClassType from the object it has been attached to.
Example Class with a MetadataType:
[MetadataType(typeof(TheMetaDataYouWantTheTypeFrom))]
public class ObjectYouWantMetaDataTypeFrom
{
public string Username { get; set; }
public string Name { get; set; }
}
public class TheMetaDataYouWantTheTypeFrom
{
[Required(ErrorMessage = "You must enter a username.")]
public object Username { get; set; }
[Required(ErrorMessage = "You must enter a name.")]
public object Name { get; set; }
}
The Code to get the MetadataClassType
Type ObjectType = ObjectYouWantMetaDataTypeFrom.GetType();
object ObjectMetaData = ObjectType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
MetadataTypeAttribute MetaData = ObjectMetaData as MetadataTypeAttribute;
if (MetaData == null)
{ throw new NullReferenceException(); }
Type metadataClassType = MetaData.MetadataClassType;
Sorry if this is a duplicate. I searched and was not able to find an answer.
How do I use reflection to get Values of Properties of class at multilevel?
I have a List of string that has some string values like this:
ClassNames = {"FirstName", "LastName", "Location.City", "Location.State", "Location.Country", "PhoneNo"}
I have two classes
public class Details
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Location Location { get; set; }
public string PhoneNo { get; set; }
}
public class Location
{
public long City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
I used reflection and I am able to get the values of firstname, lastname and phone. But how do I get the values in the location class? It throws an Error. I change the List to just have Location / City . I am missing something. I dont want to do Multiple for loops as the level might drill down to n level. (4 max to be realistic)
foreach (string fieldname in ClassNames)
{
string fieldvalue = RestultDTO[indexValue]GetType().GetProperty(fieldname) ;
}
You'd have to get the Location instance first:
var s = "Location.City";
var sVals = s.Split('.');
// here [t] is the TypeInfo of [Details]
var l = t.GetProperty(sVals[0]);
^ gets the Location PropertyInfo (really only need to do this once
var val = l.GetProperty(sVals[1]).GetValue(l.GetValue(o));
^ gets the PropertyInfo for the property you're after (City in this case)
^ gets the actual value of that property
^ gets the value of Location for o where o is an instance of Details
If you're using a version before 4.5 you'll probably need to send in one additional parameter to the GetValue method - it can be , null both times because the properties aren't indexers.
Here are two methods I wrote to solve this issue
This one gets a queue of properties from a base object:
private static Queue<PropertyInfo> GetProperty(object obj, string path)
{
Queue<PropertyInfo> values = new Queue<PropertyInfo>();
Type object_type = obj.GetType();
string[] properties = path.Split('.');
PropertyInfo propertyInfo = null;
foreach (string property in properties)
{
if (propertyInfo != null)
{
Type propertyType = propertyInfo.PropertyType;
propertyInfo = propertyType.GetProperty(property);
values.Enqueue(propertyInfo);
}
else
{
propertyInfo = object_type.GetProperty(property);
values.Enqueue(propertyInfo);
}
}
return values;
}
And this one uses the queue and the object to get the actual values:
private static T GetValue<T>(object obj, Queue<PropertyInfo> values)
{
object location = obj;
while (values.Count > 0)
{
location = values.Dequeue().GetValue(location);
}
return (T)location;
}
Probably wouldn't hurt to add some error checking and such as well.
In my WinForm application, I'm actually using the LightweightDAL project which provide a nice method to fill up my datamodel. All works fine except for a properties set as List of another model. This is my model class:
public class SchedaFamiglia
{
[DBField("Id")]
public int Id { get; set; }
[DBField("IdFamiglia")]
public int IdFamiglia { get; set; }
[DBField("IdMamma")]
public int IdMamma { get; set; }
[DBField("IdPapa")]
public int IdPapa { get; set; }
[DBField("IdBambino")]
public List<SchedaBambino> Figli { get; set; }
public SchedaFamiglia()
{
}}
The model SchedaBambino above, is just a list of base type properties (int32, string...).
And this is the snippet of GetItemFromReader method:
private static T GetItemFromReader<T>(IDataReader rdr) where T : class
{
Type type = typeof(T);
T item = Activator.CreateInstance<T>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
// for each property declared in the type provided check if the property is
// decorated with the DBField attribute
if (Attribute.IsDefined(property, typeof(DBFieldAttribute)))
{
DBFieldAttribute attrib = (DBFieldAttribute)Attribute.GetCustomAttribute(property, typeof(DBFieldAttribute));
if (Convert.IsDBNull(rdr[attrib.FieldName])) // data in database is null, so do not set the value of the property
continue;
if (property.PropertyType == rdr[attrib.FieldName].GetType()) // if the property and database field are the same
property.SetValue(item, rdr[attrib.FieldName], null); // set the value of property
else
{
// change the type of the data in table to that of property and set the value of the property
property.SetValue(item, Convert.ChangeType(rdr[attrib.FieldName], property.PropertyType), null);
}
}
}
The method above unfortunately can't handle List of generic and I've no idea how to implement.
Someone could help me?
Take this class for example:
public class Applicant : UniClass<Applicant>
{
[Key]
public int Id { get; set; }
[Field("X.838.APP.SSN")]
public string SSN { get; set; }
[Field("APP.SORT.LAST.NAME")]
public string FirstName { get; set; }
[Field("APP.SORT.FIRST.NAME")]
public string LastName { get; set; }
[Field("X.838.APP.MOST.RECENT.APPL")]
public int MostRecentApplicationId { get; set; }
}
How would I go about getting all of the properties that are decorated with the field attribute, get their types, and then assign a value to them?
This is all done with reflection. Once you have a Type object, you can then get its PropertyInfo with myType.GetProperties(), from there, you can get each property's attributes with GetCustomAttributes(), and from there if you find your attribute, you've got a winner, and then you can proceed to work with it as you please.
You already have the PropertyInfo object, so you can assign to it with PropertyInfo.SetValue(object target, object value, object[] index)
You'll need to use Reflection:
var props =
from prop in typeof(Applicant).GetProperties()
select new {
Property = prop,
Attrs = prop.GetCustomAttributes(typeof(FieldAttribute), false).Cast<FieldAttribute>()
} into propAndAttr
where propAndAttr.Attrs.Any()
select propAndAttr;
You can then iterate through this query to set the values:
foreach (var prop in props) {
var propType = prop.Property.PropertyType;
var valueToSet = GetAValueToSet(); // here's where you do whatever you need to do to determine the value that gets set
prop.Property.SetValue(applicantInstance, valueToSet, null);
}
You would just need to invoke the appropriate reflection methods - try this:
<MyApplicationInstance>.GetType().GetProperties().Where(x => x.GetCustomAttributes().Where(y => (y as FieldAttribute) != null).Count() > 0);