Reputation: 1121
THE QUESTION
If I have an array of properties of a certain Entity and I'm iterating through them, is there any way to check if the reflected type property that I am iterating in each cycle is configured as .IsRequired()
on its corresponding Entity?
EXAMPLE
This question has to be intended especially for string
properties, as in most of value types, if a db property allows null
values, then it is mapped by EF Core's Scaffolding operation as nullable type.
E.G.: a nullable int is mapped as int?
, while a not-nullable one is mapped as int
.
If I iterate through that mapped Entity's properties, to check if that property I am iterating now is a nullable one, I just have to check if myproperty.PropertyType == typeof(int?)
but...what in case of a string
type?
Is there any way to check if it's marked as an .IsRequired()
property?
MY CODE SO FAR
In my code I have the following function, which is supposed to receive as params:
objectInstance
: a Proxy derived from the Entity that I have to update, that I (have to) find previouslyvalues
: a dictionary with the property name and the new value of the properties I have to update. It could be filled with every property, or just with some of them.properties
: the array of properties of the class I previously found via reflectionsThis function is supposed to iterate through the properties array and for each property, if the new value is contained inside the dictionary, to set its new value on the instance of the class.
private static bool SetValues(Object objectInstance, Dictionary<string, object> values, PropertyInfo[] properties)
{
bool edited = false;
foreach (var item in values)
{
var temp = properties.Where(w => w.Name.ToLower() == item.Key.ToLower()).FirstOrDefault();
if (temp != null)
{
edited = true;
if (temp.PropertyType == typeof(string))
{
//here it is where I would like to do the above mentioned check
temp.SetValue(objectInstance, Convert.ToString(item.Value));
}
if (temp.PropertyType == typeof(int) || temp.PropertyType == typeof(int?))
{
temp.SetValue(objectInstance, Convert.ToInt32(item.Value));
}
if (temp.PropertyType == typeof(long) || temp.PropertyType == typeof(long?))
{
temp.SetValue(objectInstance, Convert.ToInt64(item.Value));
}
if (temp.PropertyType == typeof(decimal) || temp.PropertyType == typeof(decimal?))
{
temp.SetValue(objectInstance, Convert.ToDecimal(item.Value));
}
if (temp.PropertyType == typeof(bool) || temp.PropertyType == typeof(bool?))
{
temp.SetValue(objectInstance, Convert.ToBoolean(item.Value));
}
if (temp.PropertyType == typeof(DateTime) || temp.PropertyType == typeof(DateTime?))
{
temp.SetValue(objectInstance, Convert.ToDateTime(item.Value));
}
}
}
return edited;
}
Here's how I get "objectInstance":
var objectInstance = _context.Query(TableType).Where("Id = @0", rowKey).FirstOrDefault();
Where "Query" is an Extension:
public static IQueryable Query(this DbContext context, Type entityType) =>
(IQueryable)((IDbSetCache)context).GetOrAddSet(context.GetDependencies().SetSource, entityType);
And... an example of what I mean with a IsRequired()
-marked property of an Entity, to avoid misunderstandings:
public void Configure(EntityTypeBuilder<MyTable> builder)
{
//[a lot of properties above here...]
builder.Property(e => e.Code)
.IsRequired() //that's it!
.HasMaxLength(50)
.IsUnicode(false);
//...
}
WHAT I WOULD LIKE TO ACHIEVE
On the //here it is where I would like to do the above mentioned check
comment's position, I would like to check if (pseudocode):
if(temp.IsRequired())
{
if(String.IsNullOrWhiteSpace(Convert.ToString(item.Value)))
{
temp.SetValue(objectInstance, "");
}
else
{
temp.SetValue(objectInstance, Convert.ToString(item.Value));
}
}
else
{
if(String.IsNullOrWhiteSpace(Convert.ToString(item.Value)))
{
temp.SetValue(objectInstance, null);
}
else
{
temp.SetValue(objectInstance, Convert.ToString(item.Value));
}
}
Upvotes: 2
Views: 3053
Reputation: 205759
The proper way of doing that in EF Core is not using reflection, but the EF Core provided metadata. Which means your method should have access (receive as argument) the DbContext
(or at least IModel returned by DbContext.Model property).
Once you have it, you could use FindEntityType method to get the IEntityType containing the associated metadata with the entity class, then some of the FindProperty method overloads to get the IProperty containing the metadata associated with that property, and finally check the IsNullable property:
Gets a value indicating whether this property can contain null.
which correctly takes into account both data type, data annotations and fluent configuration.
Something like this:
private static bool SetValues(DbContext db, Object objectInstance,
Dictionary<string, object> values, PropertyInfo[] properties)
{
var entityType = db.Model.FindEntityType(objectInstance.GetType());
bool edited = false;
foreach (var item in values)
{
var property = entityType.FindProperty(item.Key);
if (property != null)
{
var propertyType = property.ClrType;
bool isRequired = !property.IsNullable;
// do something ...
}
}
}
This eliminates the need PropertyInfo[] properties
parameter.
Update: In order to work with proxy classes, instead of FindEntityType
use the FindRuntimeEntityType method.
Gets the entity that maps the given entity class, where the class may be a proxy derived from the actual entity type. Returns null if no entity type with the given CLR type is found or the entity type has a defining navigation.
Upvotes: 8
Reputation: 235
Yes, you should do like this
[IsNotNullable]
[IsPK]
[IsIdentity]
[SequenceNameAttribute("Id")]
[Required]
public Int32 Id
{
get
{
return _Id;
}
set
{
_Id = value;
}
}
var t = typeof(YourClass);
var pi = t.GetProperty("Id");
var attr = (Required[])pi.GetCustomAttributes(typeof(Required), false);
if (attr.Length > 0) {
// Use attr[0], you'll need foreach on attr if MultiUse is true
}
Upvotes: 1