LA DOMANDA
Se ho un array di proprietà di una certa Entity e sto scorrendo attraverso di esse, c'è un modo per verificare se la proprietà del tipo riflesso che sto iterando in ogni ciclo è configurata come .IsRequired()
sulla sua Entità corrispondente?
ESEMPIO
Questa domanda deve essere intesa in particolare per le proprietà string
, come nella maggior parte dei tipi di valore, se una proprietà db consente valori null
, quindi viene mappata dall'operazione Scaffolding di EF Core come tipo nullable.
Ad esempio: un valore nullo int è mappato come int?
, mentre un valore non nullable viene mappato come int
.
Se eseguo un'iterazione attraverso le proprietà di Entity mappate, per verificare se quella proprietà che sto iterando ora sia una myproperty.PropertyType == typeof(int?)
, devo solo controllare se myproperty.PropertyType == typeof(int?)
Ma ... cosa succede se si tratta di un tipo di string
?
C'è un modo per verificare se è contrassegnato come una proprietà .IsRequired()
?
IL MIO CODICE COSÌ LONTANO
Nel mio codice ho la seguente funzione, che dovrebbe ricevere come parametri:
objectInstance
: un Proxy derivato dall'entità che devo aggiornare, che ho (devo) trovare precedentemente values
: un dizionario con il nome della proprietà e il nuovo valore delle proprietà che devo aggiornare. Potrebbe essere riempito con ogni proprietà, o solo con alcuni di essi. properties
: la matrice di proprietà della classe che ho trovato in precedenza tramite riflessioni Questa funzione deve ripetere l'array di proprietà e, per ogni proprietà, se il nuovo valore è contenuto nel dizionario, per impostare il nuovo valore sull'istanza della classe.
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;
}
Ecco come ottengo "objectInstance":
var objectInstance = _context.Query(TableType).Where("Id = @0", rowKey).FirstOrDefault();
Dove "Query" è un'estensione:
public static IQueryable Query(this DbContext context, Type entityType) =>
(IQueryable)((IDbSetCache)context).GetOrAddSet(context.GetDependencies().SetSource, entityType);
E ... un esempio di cosa intendo con una proprietà IsRequired()
da IsRequired()
di un'entità, per evitare equivoci:
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);
//...
}
CHE COSA VORREI RAGGIUNGERE
Sul //here it is where I would like to do the above mentioned check
posizione del commento di //here it is where I would like to do the above mentioned check
, vorrei verificare se (pseudocodice):
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));
}
}
Il modo corretto di farlo in EF Core non è l'uso della riflessione, ma l'EF Core fornisce metadati. Il che significa che il metodo deve avere accesso (ricezione come argomento) a DbContext
(o almeno a IModel restituito dalla proprietà DbContext.Model ).
Una volta ottenuto, è possibile utilizzare il metodo FindEntityType per ottenere IEntityType che contiene i metadati associati con la classe entity, quindi alcuni overload del metodo FindProperty per ottenere l' IProperty contenente i metadati associati a tale proprietà e infine verificare la proprietà IsNullable :
Ottiene un valore che indica se questa proprietà può contenere valori nulli.
che tiene correttamente conto sia del tipo di dati, annotazioni di dati e configurazione fluente.
Qualcosa come questo:
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 ...
}
}
}
Ciò elimina la necessità del parametro PropertyInfo[] properties
.
Aggiornamento: per lavorare con le classi proxy, invece di FindEntityType
utilizzare il metodo FindRuntimeEntityType .
Ottiene l'entità che associa la classe entità data, in cui la classe può essere un proxy derivato dal tipo di entità effettivo. Restituisce null se non viene trovato alcun tipo di entità con il tipo CLR dato o il tipo di entità ha una navigazione di definizione.
Sì, dovresti fare così
[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
}