通过反射和 EF Core 检查实体的 属性 是否被标记为 IsRequired()

Check if a property of an Entity is marked as IsRequired() via reflections and EF Core

问题

如果我有一个特定实体的属性数组并且我正在遍历它们,有什么方法可以检查我在每个实体中迭代的反射类型 属性循环在其相应的实体上配置为 .IsRequired()?

示例

这个问题必须特别针对 string 属性,因为在大多数值类型中,如果数据库 属性 允许 null 值,则它由 EF Core 映射作为可空类型的脚手架操作。

例如:可为空的 int 映射为 int?,而不可为空的 int 映射为 int.

如果我遍历该映射实体的属性,检查我现在正在迭代的 属性 是否为可空属性,我只需要检查是否 myproperty.PropertyType == typeof(int?) 但是...以防万一string 类型?

有什么方法可以检查它是否被标记为 .IsRequired() 属性?

到目前为止我的代码

在我的代码中,我有以下函数,它应该作为参数接收:

此函数应该遍历属性数组,并且对于每个 属性,如果新值包含在字典中,则在 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;
}

我是这样得到的 "objectInstance":

var objectInstance = _context.Query(TableType).Where("Id = @0", rowKey).FirstOrDefault();

其中 "Query" 是扩展名:

public static IQueryable Query(this DbContext context, Type entityType) =>
            (IQueryable)((IDbSetCache)context).GetOrAddSet(context.GetDependencies().SetSource, entityType);

还有...我用 IsRequired() 标记的 属性 实体的例子,以避免误解:

 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);
    //...
 }

我想要实现什么

//here it is where I would like to do the above mentioned check评论的位置,我想检查是否(伪代码):

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));
   }
}

是的,你应该这样做

 [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
}

在 EF Core 中执行此操作的正确方法不是使用反射,而是 EF Core 提供的元数据。这意味着您的方法应该可以访问(作为参数接收)DbContext(或至少 IModel returned by DbContext.Model 属性)。

获得后,您可以使用 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 属性:

Gets a value indicating whether this property can contain null.

它正确地考虑了数据类型、数据注释和流畅的配置。

像这样:

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 ...     
        }
    }
}

这样就不需要 PropertyInfo[] properties 参数了。

更新: 为了使用代理 类,而不是 FindEntityType 使用 FindRuntimeEntityType 方法。

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.